16edb685aSTomi Valkeinen // SPDX-License-Identifier: GPL-2.0-only 26edb685aSTomi Valkeinen /* 36edb685aSTomi Valkeinen * RP1 Camera Front End Driver 46edb685aSTomi Valkeinen * 56edb685aSTomi Valkeinen * Copyright (c) 2021-2024 Raspberry Pi Ltd. 66edb685aSTomi Valkeinen * Copyright (c) 2023-2024 Ideas on Board Oy 76edb685aSTomi Valkeinen */ 86edb685aSTomi Valkeinen 96edb685aSTomi Valkeinen #include <linux/clk.h> 106edb685aSTomi Valkeinen #include <linux/debugfs.h> 116edb685aSTomi Valkeinen #include <linux/delay.h> 126edb685aSTomi Valkeinen #include <linux/device.h> 136edb685aSTomi Valkeinen #include <linux/dma-mapping.h> 146edb685aSTomi Valkeinen #include <linux/err.h> 156edb685aSTomi Valkeinen #include <linux/fwnode.h> 166edb685aSTomi Valkeinen #include <linux/init.h> 176edb685aSTomi Valkeinen #include <linux/interrupt.h> 186edb685aSTomi Valkeinen #include <linux/io.h> 196edb685aSTomi Valkeinen #include <linux/lcm.h> 206edb685aSTomi Valkeinen #include <linux/math.h> 216edb685aSTomi Valkeinen #include <linux/module.h> 226edb685aSTomi Valkeinen #include <linux/platform_device.h> 236edb685aSTomi Valkeinen #include <linux/pm_runtime.h> 246edb685aSTomi Valkeinen #include <linux/property.h> 256edb685aSTomi Valkeinen #include <linux/seq_file.h> 266edb685aSTomi Valkeinen #include <linux/slab.h> 276edb685aSTomi Valkeinen #include <linux/uaccess.h> 286edb685aSTomi Valkeinen #include <linux/videodev2.h> 296edb685aSTomi Valkeinen 306edb685aSTomi Valkeinen #include <media/v4l2-async.h> 316edb685aSTomi Valkeinen #include <media/v4l2-common.h> 326edb685aSTomi Valkeinen #include <media/v4l2-ctrls.h> 336edb685aSTomi Valkeinen #include <media/v4l2-dev.h> 346edb685aSTomi Valkeinen #include <media/v4l2-device.h> 356edb685aSTomi Valkeinen #include <media/v4l2-event.h> 366edb685aSTomi Valkeinen #include <media/v4l2-fwnode.h> 376edb685aSTomi Valkeinen #include <media/v4l2-ioctl.h> 386edb685aSTomi Valkeinen #include <media/v4l2-mc.h> 396edb685aSTomi Valkeinen #include <media/videobuf2-dma-contig.h> 406edb685aSTomi Valkeinen 416edb685aSTomi Valkeinen #include <linux/media/raspberrypi/pisp_fe_config.h> 426edb685aSTomi Valkeinen #include <linux/media/raspberrypi/pisp_fe_statistics.h> 436edb685aSTomi Valkeinen 446edb685aSTomi Valkeinen #include "cfe-fmts.h" 456edb685aSTomi Valkeinen #include "cfe.h" 466edb685aSTomi Valkeinen #include "csi2.h" 476edb685aSTomi Valkeinen #include "pisp-fe.h" 486edb685aSTomi Valkeinen 496edb685aSTomi Valkeinen #define CREATE_TRACE_POINTS 506edb685aSTomi Valkeinen #include "cfe-trace.h" 516edb685aSTomi Valkeinen 526edb685aSTomi Valkeinen #define CFE_MODULE_NAME "rp1-cfe" 536edb685aSTomi Valkeinen #define CFE_VERSION "1.0" 546edb685aSTomi Valkeinen 556edb685aSTomi Valkeinen #define cfe_dbg(cfe, fmt, arg...) dev_dbg(&(cfe)->pdev->dev, fmt, ##arg) 566edb685aSTomi Valkeinen #define cfe_info(cfe, fmt, arg...) dev_info(&(cfe)->pdev->dev, fmt, ##arg) 576edb685aSTomi Valkeinen #define cfe_err(cfe, fmt, arg...) dev_err(&(cfe)->pdev->dev, fmt, ##arg) 586edb685aSTomi Valkeinen 596edb685aSTomi Valkeinen /* MIPICFG registers */ 606edb685aSTomi Valkeinen #define MIPICFG_CFG 0x004 616edb685aSTomi Valkeinen #define MIPICFG_INTR 0x028 626edb685aSTomi Valkeinen #define MIPICFG_INTE 0x02c 636edb685aSTomi Valkeinen #define MIPICFG_INTF 0x030 646edb685aSTomi Valkeinen #define MIPICFG_INTS 0x034 656edb685aSTomi Valkeinen 666edb685aSTomi Valkeinen #define MIPICFG_CFG_SEL_CSI BIT(0) 676edb685aSTomi Valkeinen 686edb685aSTomi Valkeinen #define MIPICFG_INT_CSI_DMA BIT(0) 696edb685aSTomi Valkeinen #define MIPICFG_INT_CSI_HOST BIT(2) 706edb685aSTomi Valkeinen #define MIPICFG_INT_PISP_FE BIT(4) 716edb685aSTomi Valkeinen 726edb685aSTomi Valkeinen #define BPL_ALIGNMENT 16 736edb685aSTomi Valkeinen #define MAX_BYTESPERLINE 0xffffff00 746edb685aSTomi Valkeinen #define MAX_BUFFER_SIZE 0xffffff00 756edb685aSTomi Valkeinen /* 766edb685aSTomi Valkeinen * Max width is therefore determined by the max stride divided by the number of 776edb685aSTomi Valkeinen * bits per pixel. 786edb685aSTomi Valkeinen * 796edb685aSTomi Valkeinen * However, to avoid overflow issues let's use a 16k maximum. This lets us 806edb685aSTomi Valkeinen * calculate 16k * 16k * 4 with 32bits. If we need higher maximums, a careful 816edb685aSTomi Valkeinen * review and adjustment of the code is needed so that it will deal with 826edb685aSTomi Valkeinen * overflows correctly. 836edb685aSTomi Valkeinen */ 846edb685aSTomi Valkeinen #define MAX_WIDTH 16384 856edb685aSTomi Valkeinen #define MAX_HEIGHT MAX_WIDTH 866edb685aSTomi Valkeinen /* Define a nominal minimum image size */ 876edb685aSTomi Valkeinen #define MIN_WIDTH 16 886edb685aSTomi Valkeinen #define MIN_HEIGHT 16 896edb685aSTomi Valkeinen 906edb685aSTomi Valkeinen #define MIN_META_WIDTH 4 916edb685aSTomi Valkeinen #define MIN_META_HEIGHT 1 926edb685aSTomi Valkeinen 936edb685aSTomi Valkeinen const struct v4l2_mbus_framefmt cfe_default_format = { 946edb685aSTomi Valkeinen .width = 640, 956edb685aSTomi Valkeinen .height = 480, 966edb685aSTomi Valkeinen .code = MEDIA_BUS_FMT_SRGGB10_1X10, 976edb685aSTomi Valkeinen .field = V4L2_FIELD_NONE, 986edb685aSTomi Valkeinen .colorspace = V4L2_COLORSPACE_RAW, 996edb685aSTomi Valkeinen .ycbcr_enc = V4L2_YCBCR_ENC_601, 1006edb685aSTomi Valkeinen .quantization = V4L2_QUANTIZATION_FULL_RANGE, 1016edb685aSTomi Valkeinen .xfer_func = V4L2_XFER_FUNC_NONE, 1026edb685aSTomi Valkeinen }; 1036edb685aSTomi Valkeinen 1046edb685aSTomi Valkeinen enum node_ids { 1056edb685aSTomi Valkeinen /* CSI2 HW output nodes first. */ 1066edb685aSTomi Valkeinen CSI2_CH0, 1076edb685aSTomi Valkeinen CSI2_CH1, 1086edb685aSTomi Valkeinen CSI2_CH2, 1096edb685aSTomi Valkeinen CSI2_CH3, 1106edb685aSTomi Valkeinen /* FE only nodes from here on. */ 1116edb685aSTomi Valkeinen FE_OUT0, 1126edb685aSTomi Valkeinen FE_OUT1, 1136edb685aSTomi Valkeinen FE_STATS, 1146edb685aSTomi Valkeinen FE_CONFIG, 1156edb685aSTomi Valkeinen NUM_NODES 1166edb685aSTomi Valkeinen }; 1176edb685aSTomi Valkeinen 1186edb685aSTomi Valkeinen struct node_description { 1196edb685aSTomi Valkeinen enum node_ids id; 1206edb685aSTomi Valkeinen const char *name; 1216edb685aSTomi Valkeinen unsigned int caps; 1226edb685aSTomi Valkeinen unsigned int pad_flags; 1236edb685aSTomi Valkeinen unsigned int link_pad; 1246edb685aSTomi Valkeinen }; 1256edb685aSTomi Valkeinen 1266edb685aSTomi Valkeinen /* Must match the ordering of enum ids */ 1276edb685aSTomi Valkeinen static const struct node_description node_desc[NUM_NODES] = { 1286edb685aSTomi Valkeinen [CSI2_CH0] = { 1296edb685aSTomi Valkeinen .name = "csi2-ch0", 1306edb685aSTomi Valkeinen .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, 1316edb685aSTomi Valkeinen .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, 1326edb685aSTomi Valkeinen .link_pad = CSI2_PAD_FIRST_SOURCE + 0 1336edb685aSTomi Valkeinen }, 1346edb685aSTomi Valkeinen /* 1356edb685aSTomi Valkeinen * At the moment the main userspace component (libcamera) doesn't 1366edb685aSTomi Valkeinen * support metadata with video nodes that support both video and 1376edb685aSTomi Valkeinen * metadata. So for the time being this node is set to only support 1386edb685aSTomi Valkeinen * V4L2_CAP_META_CAPTURE. 1396edb685aSTomi Valkeinen */ 1406edb685aSTomi Valkeinen [CSI2_CH1] = { 1416edb685aSTomi Valkeinen .name = "csi2-ch1", 1426edb685aSTomi Valkeinen .caps = V4L2_CAP_META_CAPTURE, 1436edb685aSTomi Valkeinen .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, 1446edb685aSTomi Valkeinen .link_pad = CSI2_PAD_FIRST_SOURCE + 1 1456edb685aSTomi Valkeinen }, 1466edb685aSTomi Valkeinen [CSI2_CH2] = { 1476edb685aSTomi Valkeinen .name = "csi2-ch2", 1486edb685aSTomi Valkeinen .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, 1496edb685aSTomi Valkeinen .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, 1506edb685aSTomi Valkeinen .link_pad = CSI2_PAD_FIRST_SOURCE + 2 1516edb685aSTomi Valkeinen }, 1526edb685aSTomi Valkeinen [CSI2_CH3] = { 1536edb685aSTomi Valkeinen .name = "csi2-ch3", 1546edb685aSTomi Valkeinen .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE, 1556edb685aSTomi Valkeinen .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, 1566edb685aSTomi Valkeinen .link_pad = CSI2_PAD_FIRST_SOURCE + 3 1576edb685aSTomi Valkeinen }, 1586edb685aSTomi Valkeinen [FE_OUT0] = { 1596edb685aSTomi Valkeinen .name = "fe-image0", 1606edb685aSTomi Valkeinen .caps = V4L2_CAP_VIDEO_CAPTURE, 1616edb685aSTomi Valkeinen .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, 1626edb685aSTomi Valkeinen .link_pad = FE_OUTPUT0_PAD 1636edb685aSTomi Valkeinen }, 1646edb685aSTomi Valkeinen [FE_OUT1] = { 1656edb685aSTomi Valkeinen .name = "fe-image1", 1666edb685aSTomi Valkeinen .caps = V4L2_CAP_VIDEO_CAPTURE, 1676edb685aSTomi Valkeinen .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, 1686edb685aSTomi Valkeinen .link_pad = FE_OUTPUT1_PAD 1696edb685aSTomi Valkeinen }, 1706edb685aSTomi Valkeinen [FE_STATS] = { 1716edb685aSTomi Valkeinen .name = "fe-stats", 1726edb685aSTomi Valkeinen .caps = V4L2_CAP_META_CAPTURE, 1736edb685aSTomi Valkeinen .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT, 1746edb685aSTomi Valkeinen .link_pad = FE_STATS_PAD 1756edb685aSTomi Valkeinen }, 1766edb685aSTomi Valkeinen [FE_CONFIG] = { 1776edb685aSTomi Valkeinen .name = "fe-config", 1786edb685aSTomi Valkeinen .caps = V4L2_CAP_META_OUTPUT, 1796edb685aSTomi Valkeinen .pad_flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT, 1806edb685aSTomi Valkeinen .link_pad = FE_CONFIG_PAD 1816edb685aSTomi Valkeinen }, 1826edb685aSTomi Valkeinen }; 1836edb685aSTomi Valkeinen 1846edb685aSTomi Valkeinen #define is_fe_node(node) (((node)->id) >= FE_OUT0) 1856edb685aSTomi Valkeinen #define is_csi2_node(node) (!is_fe_node(node)) 1866edb685aSTomi Valkeinen 1876edb685aSTomi Valkeinen #define node_supports_image_output(node) \ 1886edb685aSTomi Valkeinen (node_desc[(node)->id].caps & V4L2_CAP_VIDEO_CAPTURE) 1896edb685aSTomi Valkeinen #define node_supports_meta_output(node) \ 1906edb685aSTomi Valkeinen (node_desc[(node)->id].caps & V4L2_CAP_META_CAPTURE) 1916edb685aSTomi Valkeinen #define node_supports_image_input(node) \ 1926edb685aSTomi Valkeinen (node_desc[(node)->id].caps & V4L2_CAP_VIDEO_OUTPUT) 1936edb685aSTomi Valkeinen #define node_supports_meta_input(node) \ 1946edb685aSTomi Valkeinen (node_desc[(node)->id].caps & V4L2_CAP_META_OUTPUT) 1956edb685aSTomi Valkeinen #define node_supports_image(node) \ 1966edb685aSTomi Valkeinen (node_supports_image_output(node) || node_supports_image_input(node)) 1976edb685aSTomi Valkeinen #define node_supports_meta(node) \ 1986edb685aSTomi Valkeinen (node_supports_meta_output(node) || node_supports_meta_input(node)) 1996edb685aSTomi Valkeinen 2006edb685aSTomi Valkeinen #define is_image_output_node(node) \ 2016edb685aSTomi Valkeinen ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 2026edb685aSTomi Valkeinen #define is_image_input_node(node) \ 2036edb685aSTomi Valkeinen ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 2046edb685aSTomi Valkeinen #define is_image_node(node) \ 2056edb685aSTomi Valkeinen (is_image_output_node(node) || is_image_input_node(node)) 2066edb685aSTomi Valkeinen #define is_meta_output_node(node) \ 2076edb685aSTomi Valkeinen ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_CAPTURE) 2086edb685aSTomi Valkeinen #define is_meta_input_node(node) \ 2096edb685aSTomi Valkeinen ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_OUTPUT) 2106edb685aSTomi Valkeinen #define is_meta_node(node) \ 2116edb685aSTomi Valkeinen (is_meta_output_node(node) || is_meta_input_node(node)) 2126edb685aSTomi Valkeinen 2136edb685aSTomi Valkeinen /* To track state across all nodes. */ 2146edb685aSTomi Valkeinen #define NODE_REGISTERED BIT(0) 2156edb685aSTomi Valkeinen #define NODE_ENABLED BIT(1) 2166edb685aSTomi Valkeinen #define NODE_STREAMING BIT(2) 2176edb685aSTomi Valkeinen #define FS_INT BIT(3) 2186edb685aSTomi Valkeinen #define FE_INT BIT(4) 2196edb685aSTomi Valkeinen #define NUM_STATES 5 2206edb685aSTomi Valkeinen 2216edb685aSTomi Valkeinen struct cfe_buffer { 2226edb685aSTomi Valkeinen struct vb2_v4l2_buffer vb; 2236edb685aSTomi Valkeinen struct list_head list; 2246edb685aSTomi Valkeinen }; 2256edb685aSTomi Valkeinen 2266edb685aSTomi Valkeinen struct cfe_config_buffer { 2276edb685aSTomi Valkeinen struct cfe_buffer buf; 2286edb685aSTomi Valkeinen struct pisp_fe_config config; 2296edb685aSTomi Valkeinen }; 2306edb685aSTomi Valkeinen 2316edb685aSTomi Valkeinen static inline struct cfe_buffer *to_cfe_buffer(struct vb2_buffer *vb) 2326edb685aSTomi Valkeinen { 2336edb685aSTomi Valkeinen return container_of(vb, struct cfe_buffer, vb.vb2_buf); 2346edb685aSTomi Valkeinen } 2356edb685aSTomi Valkeinen 2366edb685aSTomi Valkeinen static inline 2376edb685aSTomi Valkeinen struct cfe_config_buffer *to_cfe_config_buffer(struct cfe_buffer *buf) 2386edb685aSTomi Valkeinen { 2396edb685aSTomi Valkeinen return container_of(buf, struct cfe_config_buffer, buf); 2406edb685aSTomi Valkeinen } 2416edb685aSTomi Valkeinen 2426edb685aSTomi Valkeinen struct cfe_node { 2436edb685aSTomi Valkeinen /* Node id */ 2446edb685aSTomi Valkeinen enum node_ids id; 2456edb685aSTomi Valkeinen /* Pointer pointing to current v4l2_buffer */ 2466edb685aSTomi Valkeinen struct cfe_buffer *cur_frm; 2476edb685aSTomi Valkeinen /* Pointer pointing to next v4l2_buffer */ 2486edb685aSTomi Valkeinen struct cfe_buffer *next_frm; 2496edb685aSTomi Valkeinen /* Used to store current pixel format */ 2506edb685aSTomi Valkeinen struct v4l2_format vid_fmt; 2516edb685aSTomi Valkeinen /* Used to store current meta format */ 2526edb685aSTomi Valkeinen struct v4l2_format meta_fmt; 2536edb685aSTomi Valkeinen /* Buffer queue used in video-buf */ 2546edb685aSTomi Valkeinen struct vb2_queue buffer_queue; 2556edb685aSTomi Valkeinen /* Queue of filled frames */ 2566edb685aSTomi Valkeinen struct list_head dma_queue; 2576edb685aSTomi Valkeinen /* lock used to access this structure */ 2586edb685aSTomi Valkeinen struct mutex lock; 2596edb685aSTomi Valkeinen /* Identifies video device for this channel */ 2606edb685aSTomi Valkeinen struct video_device video_dev; 2616edb685aSTomi Valkeinen /* Pointer to the parent handle */ 2626edb685aSTomi Valkeinen struct cfe_device *cfe; 2636edb685aSTomi Valkeinen /* Media pad for this node */ 2646edb685aSTomi Valkeinen struct media_pad pad; 2656edb685aSTomi Valkeinen /* Frame-start counter */ 2666edb685aSTomi Valkeinen unsigned int fs_count; 2676edb685aSTomi Valkeinen /* Timestamp of the current buffer */ 2686edb685aSTomi Valkeinen u64 ts; 2696edb685aSTomi Valkeinen }; 2706edb685aSTomi Valkeinen 2716edb685aSTomi Valkeinen struct cfe_device { 2726edb685aSTomi Valkeinen struct dentry *debugfs; 2736edb685aSTomi Valkeinen struct kref kref; 2746edb685aSTomi Valkeinen 2756edb685aSTomi Valkeinen /* peripheral base address */ 2766edb685aSTomi Valkeinen void __iomem *mipi_cfg_base; 2776edb685aSTomi Valkeinen 2786edb685aSTomi Valkeinen struct clk *clk; 2796edb685aSTomi Valkeinen 2806edb685aSTomi Valkeinen /* V4l2 device */ 2816edb685aSTomi Valkeinen struct v4l2_device v4l2_dev; 2826edb685aSTomi Valkeinen struct media_device mdev; 2836edb685aSTomi Valkeinen struct media_pipeline pipe; 2846edb685aSTomi Valkeinen 2856edb685aSTomi Valkeinen /* IRQ lock for node state and DMA queues */ 2866edb685aSTomi Valkeinen spinlock_t state_lock; 2876edb685aSTomi Valkeinen bool job_ready; 2886edb685aSTomi Valkeinen bool job_queued; 2896edb685aSTomi Valkeinen 2906edb685aSTomi Valkeinen /* parent device */ 2916edb685aSTomi Valkeinen struct platform_device *pdev; 2926edb685aSTomi Valkeinen /* subdevice async Notifier */ 2936edb685aSTomi Valkeinen struct v4l2_async_notifier notifier; 2946edb685aSTomi Valkeinen 2956edb685aSTomi Valkeinen /* Source sub device */ 2966edb685aSTomi Valkeinen struct v4l2_subdev *source_sd; 2976edb685aSTomi Valkeinen /* Source subdev's pad */ 2986edb685aSTomi Valkeinen u32 source_pad; 2996edb685aSTomi Valkeinen 3006edb685aSTomi Valkeinen struct cfe_node node[NUM_NODES]; 3016edb685aSTomi Valkeinen DECLARE_BITMAP(node_flags, NUM_STATES * NUM_NODES); 3026edb685aSTomi Valkeinen 3036edb685aSTomi Valkeinen struct csi2_device csi2; 3046edb685aSTomi Valkeinen struct pisp_fe_device fe; 3056edb685aSTomi Valkeinen 3066edb685aSTomi Valkeinen int fe_csi2_channel; 3076edb685aSTomi Valkeinen 3086edb685aSTomi Valkeinen /* Mask of enabled streams */ 3096edb685aSTomi Valkeinen u64 streams_mask; 3106edb685aSTomi Valkeinen }; 3116edb685aSTomi Valkeinen 3126edb685aSTomi Valkeinen static inline bool is_fe_enabled(struct cfe_device *cfe) 3136edb685aSTomi Valkeinen { 3146edb685aSTomi Valkeinen return cfe->fe_csi2_channel != -1; 3156edb685aSTomi Valkeinen } 3166edb685aSTomi Valkeinen 3176edb685aSTomi Valkeinen static inline struct cfe_device *to_cfe_device(struct v4l2_device *v4l2_dev) 3186edb685aSTomi Valkeinen { 3196edb685aSTomi Valkeinen return container_of(v4l2_dev, struct cfe_device, v4l2_dev); 3206edb685aSTomi Valkeinen } 3216edb685aSTomi Valkeinen 3226edb685aSTomi Valkeinen static inline u32 cfg_reg_read(struct cfe_device *cfe, u32 offset) 3236edb685aSTomi Valkeinen { 3246edb685aSTomi Valkeinen return readl(cfe->mipi_cfg_base + offset); 3256edb685aSTomi Valkeinen } 3266edb685aSTomi Valkeinen 3276edb685aSTomi Valkeinen static inline void cfg_reg_write(struct cfe_device *cfe, u32 offset, u32 val) 3286edb685aSTomi Valkeinen { 3296edb685aSTomi Valkeinen writel(val, cfe->mipi_cfg_base + offset); 3306edb685aSTomi Valkeinen } 3316edb685aSTomi Valkeinen 3326edb685aSTomi Valkeinen static bool check_state(struct cfe_device *cfe, unsigned long state, 3336edb685aSTomi Valkeinen unsigned int node_id) 3346edb685aSTomi Valkeinen { 3356edb685aSTomi Valkeinen unsigned long bit; 3366edb685aSTomi Valkeinen 3376edb685aSTomi Valkeinen for_each_set_bit(bit, &state, sizeof(state)) { 3386edb685aSTomi Valkeinen if (!test_bit(bit + (node_id * NUM_STATES), cfe->node_flags)) 3396edb685aSTomi Valkeinen return false; 3406edb685aSTomi Valkeinen } 3416edb685aSTomi Valkeinen 3426edb685aSTomi Valkeinen return true; 3436edb685aSTomi Valkeinen } 3446edb685aSTomi Valkeinen 3456edb685aSTomi Valkeinen static void set_state(struct cfe_device *cfe, unsigned long state, 3466edb685aSTomi Valkeinen unsigned int node_id) 3476edb685aSTomi Valkeinen { 3486edb685aSTomi Valkeinen unsigned long bit; 3496edb685aSTomi Valkeinen 3506edb685aSTomi Valkeinen for_each_set_bit(bit, &state, sizeof(state)) 3516edb685aSTomi Valkeinen set_bit(bit + (node_id * NUM_STATES), cfe->node_flags); 3526edb685aSTomi Valkeinen } 3536edb685aSTomi Valkeinen 3546edb685aSTomi Valkeinen static void clear_state(struct cfe_device *cfe, unsigned long state, 3556edb685aSTomi Valkeinen unsigned int node_id) 3566edb685aSTomi Valkeinen { 3576edb685aSTomi Valkeinen unsigned long bit; 3586edb685aSTomi Valkeinen 3596edb685aSTomi Valkeinen for_each_set_bit(bit, &state, sizeof(state)) 3606edb685aSTomi Valkeinen clear_bit(bit + (node_id * NUM_STATES), cfe->node_flags); 3616edb685aSTomi Valkeinen } 3626edb685aSTomi Valkeinen 3636edb685aSTomi Valkeinen static bool test_any_node(struct cfe_device *cfe, unsigned long cond) 3646edb685aSTomi Valkeinen { 3656edb685aSTomi Valkeinen for (unsigned int i = 0; i < NUM_NODES; i++) { 3666edb685aSTomi Valkeinen if (check_state(cfe, cond, i)) 3676edb685aSTomi Valkeinen return true; 3686edb685aSTomi Valkeinen } 3696edb685aSTomi Valkeinen 3706edb685aSTomi Valkeinen return false; 3716edb685aSTomi Valkeinen } 3726edb685aSTomi Valkeinen 3736edb685aSTomi Valkeinen static bool test_all_nodes(struct cfe_device *cfe, unsigned long precond, 3746edb685aSTomi Valkeinen unsigned long cond) 3756edb685aSTomi Valkeinen { 3766edb685aSTomi Valkeinen for (unsigned int i = 0; i < NUM_NODES; i++) { 3776edb685aSTomi Valkeinen if (check_state(cfe, precond, i)) { 3786edb685aSTomi Valkeinen if (!check_state(cfe, cond, i)) 3796edb685aSTomi Valkeinen return false; 3806edb685aSTomi Valkeinen } 3816edb685aSTomi Valkeinen } 3826edb685aSTomi Valkeinen 3836edb685aSTomi Valkeinen return true; 3846edb685aSTomi Valkeinen } 3856edb685aSTomi Valkeinen 3866edb685aSTomi Valkeinen static int mipi_cfg_regs_show(struct seq_file *s, void *data) 3876edb685aSTomi Valkeinen { 3886edb685aSTomi Valkeinen struct cfe_device *cfe = s->private; 3896edb685aSTomi Valkeinen int ret; 3906edb685aSTomi Valkeinen 3916edb685aSTomi Valkeinen ret = pm_runtime_resume_and_get(&cfe->pdev->dev); 3926edb685aSTomi Valkeinen if (ret) 3936edb685aSTomi Valkeinen return ret; 3946edb685aSTomi Valkeinen 3956edb685aSTomi Valkeinen #define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", cfg_reg_read(cfe, reg)) 3966edb685aSTomi Valkeinen DUMP(MIPICFG_CFG); 3976edb685aSTomi Valkeinen DUMP(MIPICFG_INTR); 3986edb685aSTomi Valkeinen DUMP(MIPICFG_INTE); 3996edb685aSTomi Valkeinen DUMP(MIPICFG_INTF); 4006edb685aSTomi Valkeinen DUMP(MIPICFG_INTS); 4016edb685aSTomi Valkeinen #undef DUMP 4026edb685aSTomi Valkeinen 4036edb685aSTomi Valkeinen pm_runtime_put(&cfe->pdev->dev); 4046edb685aSTomi Valkeinen 4056edb685aSTomi Valkeinen return 0; 4066edb685aSTomi Valkeinen } 4076edb685aSTomi Valkeinen 4086edb685aSTomi Valkeinen DEFINE_SHOW_ATTRIBUTE(mipi_cfg_regs); 4096edb685aSTomi Valkeinen 4106edb685aSTomi Valkeinen /* Format setup functions */ 4116edb685aSTomi Valkeinen const struct cfe_fmt *find_format_by_code(u32 code) 4126edb685aSTomi Valkeinen { 4136edb685aSTomi Valkeinen for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { 4146edb685aSTomi Valkeinen if (formats[i].code == code) 4156edb685aSTomi Valkeinen return &formats[i]; 4166edb685aSTomi Valkeinen } 4176edb685aSTomi Valkeinen 4186edb685aSTomi Valkeinen return NULL; 4196edb685aSTomi Valkeinen } 4206edb685aSTomi Valkeinen 4216edb685aSTomi Valkeinen const struct cfe_fmt *find_format_by_pix(u32 pixelformat) 4226edb685aSTomi Valkeinen { 4236edb685aSTomi Valkeinen for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { 4246edb685aSTomi Valkeinen if (formats[i].fourcc == pixelformat) 4256edb685aSTomi Valkeinen return &formats[i]; 4266edb685aSTomi Valkeinen } 4276edb685aSTomi Valkeinen 4286edb685aSTomi Valkeinen return NULL; 4296edb685aSTomi Valkeinen } 4306edb685aSTomi Valkeinen 4316edb685aSTomi Valkeinen static const struct cfe_fmt *find_format_by_code_and_fourcc(u32 code, 4326edb685aSTomi Valkeinen u32 fourcc) 4336edb685aSTomi Valkeinen { 4346edb685aSTomi Valkeinen for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { 4356edb685aSTomi Valkeinen if (formats[i].code == code && formats[i].fourcc == fourcc) 4366edb685aSTomi Valkeinen return &formats[i]; 4376edb685aSTomi Valkeinen } 4386edb685aSTomi Valkeinen 4396edb685aSTomi Valkeinen return NULL; 4406edb685aSTomi Valkeinen } 4416edb685aSTomi Valkeinen 4426edb685aSTomi Valkeinen /* 4436edb685aSTomi Valkeinen * Given the mbus code, find the 16 bit remapped code. Returns 0 if no remap 4446edb685aSTomi Valkeinen * possible. 4456edb685aSTomi Valkeinen */ 4466edb685aSTomi Valkeinen u32 cfe_find_16bit_code(u32 code) 4476edb685aSTomi Valkeinen { 4486edb685aSTomi Valkeinen const struct cfe_fmt *cfe_fmt; 4496edb685aSTomi Valkeinen 4506edb685aSTomi Valkeinen cfe_fmt = find_format_by_code(code); 4516edb685aSTomi Valkeinen 4526edb685aSTomi Valkeinen if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_16BIT]) 4536edb685aSTomi Valkeinen return 0; 4546edb685aSTomi Valkeinen 4556edb685aSTomi Valkeinen cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_16BIT]); 4566edb685aSTomi Valkeinen if (!cfe_fmt) 4576edb685aSTomi Valkeinen return 0; 4586edb685aSTomi Valkeinen 4596edb685aSTomi Valkeinen return cfe_fmt->code; 4606edb685aSTomi Valkeinen } 4616edb685aSTomi Valkeinen 4626edb685aSTomi Valkeinen /* 4636edb685aSTomi Valkeinen * Given the mbus code, find the 8 bit compressed code. Returns 0 if no remap 4646edb685aSTomi Valkeinen * possible. 4656edb685aSTomi Valkeinen */ 4666edb685aSTomi Valkeinen u32 cfe_find_compressed_code(u32 code) 4676edb685aSTomi Valkeinen { 4686edb685aSTomi Valkeinen const struct cfe_fmt *cfe_fmt; 4696edb685aSTomi Valkeinen 4706edb685aSTomi Valkeinen cfe_fmt = find_format_by_code(code); 4716edb685aSTomi Valkeinen 4726edb685aSTomi Valkeinen if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_COMPRESSED]) 4736edb685aSTomi Valkeinen return 0; 4746edb685aSTomi Valkeinen 4756edb685aSTomi Valkeinen cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_COMPRESSED]); 4766edb685aSTomi Valkeinen if (!cfe_fmt) 4776edb685aSTomi Valkeinen return 0; 4786edb685aSTomi Valkeinen 4796edb685aSTomi Valkeinen return cfe_fmt->code; 4806edb685aSTomi Valkeinen } 4816edb685aSTomi Valkeinen 4826edb685aSTomi Valkeinen static void cfe_calc_vid_format_size_bpl(struct cfe_device *cfe, 4836edb685aSTomi Valkeinen const struct cfe_fmt *fmt, 4846edb685aSTomi Valkeinen struct v4l2_format *f) 4856edb685aSTomi Valkeinen { 4866edb685aSTomi Valkeinen unsigned int min_bytesperline; 4876edb685aSTomi Valkeinen 4886edb685aSTomi Valkeinen v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2, 4896edb685aSTomi Valkeinen &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0, 0); 4906edb685aSTomi Valkeinen 4916edb685aSTomi Valkeinen min_bytesperline = 4926edb685aSTomi Valkeinen ALIGN((f->fmt.pix.width * fmt->depth) >> 3, BPL_ALIGNMENT); 4936edb685aSTomi Valkeinen 4946edb685aSTomi Valkeinen if (f->fmt.pix.bytesperline > min_bytesperline && 4956edb685aSTomi Valkeinen f->fmt.pix.bytesperline <= MAX_BYTESPERLINE) 4966edb685aSTomi Valkeinen f->fmt.pix.bytesperline = 4976edb685aSTomi Valkeinen ALIGN(f->fmt.pix.bytesperline, BPL_ALIGNMENT); 4986edb685aSTomi Valkeinen else 4996edb685aSTomi Valkeinen f->fmt.pix.bytesperline = min_bytesperline; 5006edb685aSTomi Valkeinen 5016edb685aSTomi Valkeinen f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 5026edb685aSTomi Valkeinen 5036edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: %p4cc size: %ux%u bpl:%u img_size:%u\n", __func__, 5046edb685aSTomi Valkeinen &f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height, 5056edb685aSTomi Valkeinen f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); 5066edb685aSTomi Valkeinen } 5076edb685aSTomi Valkeinen 5086edb685aSTomi Valkeinen static void cfe_calc_meta_format_size_bpl(struct cfe_device *cfe, 5096edb685aSTomi Valkeinen const struct cfe_fmt *fmt, 5106edb685aSTomi Valkeinen struct v4l2_format *f) 5116edb685aSTomi Valkeinen { 5126edb685aSTomi Valkeinen v4l_bound_align_image(&f->fmt.meta.width, MIN_META_WIDTH, MAX_WIDTH, 2, 5136edb685aSTomi Valkeinen &f->fmt.meta.height, MIN_META_HEIGHT, MAX_HEIGHT, 5146edb685aSTomi Valkeinen 0, 0); 5156edb685aSTomi Valkeinen 5166edb685aSTomi Valkeinen f->fmt.meta.bytesperline = (f->fmt.meta.width * fmt->depth) >> 3; 5176edb685aSTomi Valkeinen f->fmt.meta.buffersize = f->fmt.meta.height * f->fmt.pix.bytesperline; 5186edb685aSTomi Valkeinen 5196edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: %p4cc size: %ux%u bpl:%u buf_size:%u\n", __func__, 5206edb685aSTomi Valkeinen &f->fmt.meta.dataformat, f->fmt.meta.width, f->fmt.meta.height, 5216edb685aSTomi Valkeinen f->fmt.meta.bytesperline, f->fmt.meta.buffersize); 5226edb685aSTomi Valkeinen } 5236edb685aSTomi Valkeinen 5246edb685aSTomi Valkeinen static void cfe_schedule_next_csi2_job(struct cfe_device *cfe) 5256edb685aSTomi Valkeinen { 5266edb685aSTomi Valkeinen struct cfe_buffer *buf; 5276edb685aSTomi Valkeinen dma_addr_t addr; 5286edb685aSTomi Valkeinen 5296edb685aSTomi Valkeinen for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) { 5306edb685aSTomi Valkeinen struct cfe_node *node = &cfe->node[i]; 5316edb685aSTomi Valkeinen unsigned int stride, size; 5326edb685aSTomi Valkeinen 5336edb685aSTomi Valkeinen if (!check_state(cfe, NODE_STREAMING, i)) 5346edb685aSTomi Valkeinen continue; 5356edb685aSTomi Valkeinen 5366edb685aSTomi Valkeinen buf = list_first_entry(&node->dma_queue, struct cfe_buffer, 5376edb685aSTomi Valkeinen list); 5386edb685aSTomi Valkeinen node->next_frm = buf; 5396edb685aSTomi Valkeinen list_del(&buf->list); 5406edb685aSTomi Valkeinen 5416edb685aSTomi Valkeinen trace_cfe_csi2_schedule(node->id, &buf->vb.vb2_buf); 5426edb685aSTomi Valkeinen 5436edb685aSTomi Valkeinen if (is_meta_node(node)) { 5446edb685aSTomi Valkeinen size = node->meta_fmt.fmt.meta.buffersize; 5456edb685aSTomi Valkeinen /* We use CSI2_CH_CTRL_PACK_BYTES, so stride == 0 */ 5466edb685aSTomi Valkeinen stride = 0; 5476edb685aSTomi Valkeinen } else { 5486edb685aSTomi Valkeinen size = node->vid_fmt.fmt.pix.sizeimage; 5496edb685aSTomi Valkeinen stride = node->vid_fmt.fmt.pix.bytesperline; 5506edb685aSTomi Valkeinen } 5516edb685aSTomi Valkeinen 5526edb685aSTomi Valkeinen addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); 5536edb685aSTomi Valkeinen csi2_set_buffer(&cfe->csi2, node->id, addr, stride, size); 5546edb685aSTomi Valkeinen } 5556edb685aSTomi Valkeinen } 5566edb685aSTomi Valkeinen 5576edb685aSTomi Valkeinen static void cfe_schedule_next_pisp_job(struct cfe_device *cfe) 5586edb685aSTomi Valkeinen { 5596edb685aSTomi Valkeinen struct vb2_buffer *vb2_bufs[FE_NUM_PADS] = { 0 }; 5606edb685aSTomi Valkeinen struct cfe_config_buffer *config_buf; 5616edb685aSTomi Valkeinen struct cfe_buffer *buf; 5626edb685aSTomi Valkeinen 5636edb685aSTomi Valkeinen for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) { 5646edb685aSTomi Valkeinen struct cfe_node *node = &cfe->node[i]; 5656edb685aSTomi Valkeinen 5666edb685aSTomi Valkeinen if (!check_state(cfe, NODE_STREAMING, i)) 5676edb685aSTomi Valkeinen continue; 5686edb685aSTomi Valkeinen 5696edb685aSTomi Valkeinen buf = list_first_entry(&node->dma_queue, struct cfe_buffer, 5706edb685aSTomi Valkeinen list); 5716edb685aSTomi Valkeinen 5726edb685aSTomi Valkeinen trace_cfe_fe_schedule(node->id, &buf->vb.vb2_buf); 5736edb685aSTomi Valkeinen 5746edb685aSTomi Valkeinen node->next_frm = buf; 5756edb685aSTomi Valkeinen vb2_bufs[node_desc[i].link_pad] = &buf->vb.vb2_buf; 5766edb685aSTomi Valkeinen list_del(&buf->list); 5776edb685aSTomi Valkeinen } 5786edb685aSTomi Valkeinen 5796edb685aSTomi Valkeinen config_buf = to_cfe_config_buffer(cfe->node[FE_CONFIG].next_frm); 5806edb685aSTomi Valkeinen pisp_fe_submit_job(&cfe->fe, vb2_bufs, &config_buf->config); 5816edb685aSTomi Valkeinen } 5826edb685aSTomi Valkeinen 5836edb685aSTomi Valkeinen static bool cfe_check_job_ready(struct cfe_device *cfe) 5846edb685aSTomi Valkeinen { 5856edb685aSTomi Valkeinen for (unsigned int i = 0; i < NUM_NODES; i++) { 5866edb685aSTomi Valkeinen struct cfe_node *node = &cfe->node[i]; 5876edb685aSTomi Valkeinen 5886edb685aSTomi Valkeinen if (!check_state(cfe, NODE_ENABLED, i)) 5896edb685aSTomi Valkeinen continue; 5906edb685aSTomi Valkeinen 5916edb685aSTomi Valkeinen if (list_empty(&node->dma_queue)) 5926edb685aSTomi Valkeinen return false; 5936edb685aSTomi Valkeinen } 5946edb685aSTomi Valkeinen 5956edb685aSTomi Valkeinen return true; 5966edb685aSTomi Valkeinen } 5976edb685aSTomi Valkeinen 5986edb685aSTomi Valkeinen static void cfe_prepare_next_job(struct cfe_device *cfe) 5996edb685aSTomi Valkeinen { 6006edb685aSTomi Valkeinen trace_cfe_prepare_next_job(is_fe_enabled(cfe)); 6016edb685aSTomi Valkeinen 6026edb685aSTomi Valkeinen cfe->job_queued = true; 6036edb685aSTomi Valkeinen cfe_schedule_next_csi2_job(cfe); 6046edb685aSTomi Valkeinen if (is_fe_enabled(cfe)) 6056edb685aSTomi Valkeinen cfe_schedule_next_pisp_job(cfe); 6066edb685aSTomi Valkeinen 6076edb685aSTomi Valkeinen /* Flag if another job is ready after this. */ 6086edb685aSTomi Valkeinen cfe->job_ready = cfe_check_job_ready(cfe); 6096edb685aSTomi Valkeinen } 6106edb685aSTomi Valkeinen 6116edb685aSTomi Valkeinen static void cfe_process_buffer_complete(struct cfe_node *node, 6126edb685aSTomi Valkeinen enum vb2_buffer_state state) 6136edb685aSTomi Valkeinen { 6146edb685aSTomi Valkeinen trace_cfe_buffer_complete(node->id, &node->cur_frm->vb); 6156edb685aSTomi Valkeinen 6166edb685aSTomi Valkeinen node->cur_frm->vb.sequence = node->fs_count - 1; 6176edb685aSTomi Valkeinen vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state); 6186edb685aSTomi Valkeinen } 6196edb685aSTomi Valkeinen 6206edb685aSTomi Valkeinen static void cfe_queue_event_sof(struct cfe_node *node) 6216edb685aSTomi Valkeinen { 6226edb685aSTomi Valkeinen struct v4l2_event event = { 6236edb685aSTomi Valkeinen .type = V4L2_EVENT_FRAME_SYNC, 6246edb685aSTomi Valkeinen .u.frame_sync.frame_sequence = node->fs_count - 1, 6256edb685aSTomi Valkeinen }; 6266edb685aSTomi Valkeinen 6276edb685aSTomi Valkeinen v4l2_event_queue(&node->video_dev, &event); 6286edb685aSTomi Valkeinen } 6296edb685aSTomi Valkeinen 6306edb685aSTomi Valkeinen static void cfe_sof_isr(struct cfe_node *node) 6316edb685aSTomi Valkeinen { 6326edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 6336edb685aSTomi Valkeinen bool matching_fs = true; 6346edb685aSTomi Valkeinen 6356edb685aSTomi Valkeinen trace_cfe_frame_start(node->id, node->fs_count); 6366edb685aSTomi Valkeinen 6376edb685aSTomi Valkeinen /* 6386edb685aSTomi Valkeinen * If the sensor is producing unexpected frame event ordering over a 6396edb685aSTomi Valkeinen * sustained period of time, guard against the possibility of coming 6406edb685aSTomi Valkeinen * here and orphaning the cur_frm if it's not been dequeued already. 6416edb685aSTomi Valkeinen * Unfortunately, there is not enough hardware state to tell if this 6426edb685aSTomi Valkeinen * may have occurred. 6436edb685aSTomi Valkeinen */ 644fba1aff8SColin Ian King if (WARN(node->cur_frm, "%s: [%s] Orphaned frame at seq %u\n", 6456edb685aSTomi Valkeinen __func__, node_desc[node->id].name, node->fs_count)) 6466edb685aSTomi Valkeinen cfe_process_buffer_complete(node, VB2_BUF_STATE_ERROR); 6476edb685aSTomi Valkeinen 6486edb685aSTomi Valkeinen node->cur_frm = node->next_frm; 6496edb685aSTomi Valkeinen node->next_frm = NULL; 6506edb685aSTomi Valkeinen node->fs_count++; 6516edb685aSTomi Valkeinen 6526edb685aSTomi Valkeinen node->ts = ktime_get_ns(); 6536edb685aSTomi Valkeinen for (unsigned int i = 0; i < NUM_NODES; i++) { 6546edb685aSTomi Valkeinen if (!check_state(cfe, NODE_STREAMING, i) || i == node->id) 6556edb685aSTomi Valkeinen continue; 6566edb685aSTomi Valkeinen /* 6576edb685aSTomi Valkeinen * This checks if any other node has seen a FS. If yes, use the 6586edb685aSTomi Valkeinen * same timestamp, eventually across all node buffers. 6596edb685aSTomi Valkeinen */ 6606edb685aSTomi Valkeinen if (cfe->node[i].fs_count >= node->fs_count) 6616edb685aSTomi Valkeinen node->ts = cfe->node[i].ts; 6626edb685aSTomi Valkeinen /* 6636edb685aSTomi Valkeinen * This checks if all other node have seen a matching FS. If 6646edb685aSTomi Valkeinen * yes, we can flag another job to be queued. 6656edb685aSTomi Valkeinen */ 6666edb685aSTomi Valkeinen if (matching_fs && cfe->node[i].fs_count != node->fs_count) 6676edb685aSTomi Valkeinen matching_fs = false; 6686edb685aSTomi Valkeinen } 6696edb685aSTomi Valkeinen 6706edb685aSTomi Valkeinen if (matching_fs) 6716edb685aSTomi Valkeinen cfe->job_queued = false; 6726edb685aSTomi Valkeinen 6736edb685aSTomi Valkeinen if (node->cur_frm) 6746edb685aSTomi Valkeinen node->cur_frm->vb.vb2_buf.timestamp = node->ts; 6756edb685aSTomi Valkeinen 6766edb685aSTomi Valkeinen set_state(cfe, FS_INT, node->id); 6776edb685aSTomi Valkeinen clear_state(cfe, FE_INT, node->id); 6786edb685aSTomi Valkeinen 6796edb685aSTomi Valkeinen if (is_image_output_node(node)) 6806edb685aSTomi Valkeinen cfe_queue_event_sof(node); 6816edb685aSTomi Valkeinen } 6826edb685aSTomi Valkeinen 6836edb685aSTomi Valkeinen static void cfe_eof_isr(struct cfe_node *node) 6846edb685aSTomi Valkeinen { 6856edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 6866edb685aSTomi Valkeinen 6876edb685aSTomi Valkeinen trace_cfe_frame_end(node->id, node->fs_count - 1); 6886edb685aSTomi Valkeinen 6896edb685aSTomi Valkeinen if (node->cur_frm) 6906edb685aSTomi Valkeinen cfe_process_buffer_complete(node, VB2_BUF_STATE_DONE); 6916edb685aSTomi Valkeinen 6926edb685aSTomi Valkeinen node->cur_frm = NULL; 6936edb685aSTomi Valkeinen set_state(cfe, FE_INT, node->id); 6946edb685aSTomi Valkeinen clear_state(cfe, FS_INT, node->id); 6956edb685aSTomi Valkeinen } 6966edb685aSTomi Valkeinen 6976edb685aSTomi Valkeinen static irqreturn_t cfe_isr(int irq, void *dev) 6986edb685aSTomi Valkeinen { 6996edb685aSTomi Valkeinen struct cfe_device *cfe = dev; 7006edb685aSTomi Valkeinen bool sof[NUM_NODES] = { 0 }, eof[NUM_NODES] = { 0 }; 7016edb685aSTomi Valkeinen u32 sts; 7026edb685aSTomi Valkeinen 7036edb685aSTomi Valkeinen sts = cfg_reg_read(cfe, MIPICFG_INTS); 7046edb685aSTomi Valkeinen 7056edb685aSTomi Valkeinen if (sts & MIPICFG_INT_CSI_DMA) 7066edb685aSTomi Valkeinen csi2_isr(&cfe->csi2, sof, eof); 7076edb685aSTomi Valkeinen 7086edb685aSTomi Valkeinen if (sts & MIPICFG_INT_PISP_FE) 7096edb685aSTomi Valkeinen pisp_fe_isr(&cfe->fe, sof + CSI2_NUM_CHANNELS, 7106edb685aSTomi Valkeinen eof + CSI2_NUM_CHANNELS); 7116edb685aSTomi Valkeinen 7126edb685aSTomi Valkeinen spin_lock(&cfe->state_lock); 7136edb685aSTomi Valkeinen 7146edb685aSTomi Valkeinen for (unsigned int i = 0; i < NUM_NODES; i++) { 7156edb685aSTomi Valkeinen struct cfe_node *node = &cfe->node[i]; 7166edb685aSTomi Valkeinen 7176edb685aSTomi Valkeinen /* 7186edb685aSTomi Valkeinen * The check_state(NODE_STREAMING) is to ensure we do not loop 7196edb685aSTomi Valkeinen * over the CSI2_CHx nodes when the FE is active since they 7206edb685aSTomi Valkeinen * generate interrupts even though the node is not streaming. 7216edb685aSTomi Valkeinen */ 7226edb685aSTomi Valkeinen if (!check_state(cfe, NODE_STREAMING, i) || !(sof[i] || eof[i])) 7236edb685aSTomi Valkeinen continue; 7246edb685aSTomi Valkeinen 7256edb685aSTomi Valkeinen /* 7266edb685aSTomi Valkeinen * There are 3 cases where we could get FS + FE_ACK at 7276edb685aSTomi Valkeinen * the same time: 7286edb685aSTomi Valkeinen * 1) FE of the current frame, and FS of the next frame. 7296edb685aSTomi Valkeinen * 2) FS + FE of the same frame. 7306edb685aSTomi Valkeinen * 3) FE of the current frame, and FS + FE of the next 7316edb685aSTomi Valkeinen * frame. To handle this, see the sof handler below. 7326edb685aSTomi Valkeinen * 7336edb685aSTomi Valkeinen * (1) is handled implicitly by the ordering of the FE and FS 7346edb685aSTomi Valkeinen * handlers below. 7356edb685aSTomi Valkeinen */ 7366edb685aSTomi Valkeinen if (eof[i]) { 7376edb685aSTomi Valkeinen /* 7386edb685aSTomi Valkeinen * The condition below tests for (2). Run the FS handler 7396edb685aSTomi Valkeinen * first before the FE handler, both for the current 7406edb685aSTomi Valkeinen * frame. 7416edb685aSTomi Valkeinen */ 7426edb685aSTomi Valkeinen if (sof[i] && !check_state(cfe, FS_INT, i)) { 7436edb685aSTomi Valkeinen cfe_sof_isr(node); 7446edb685aSTomi Valkeinen sof[i] = false; 7456edb685aSTomi Valkeinen } 7466edb685aSTomi Valkeinen 7476edb685aSTomi Valkeinen cfe_eof_isr(node); 7486edb685aSTomi Valkeinen } 7496edb685aSTomi Valkeinen 7506edb685aSTomi Valkeinen if (sof[i]) { 7516edb685aSTomi Valkeinen /* 7526edb685aSTomi Valkeinen * The condition below tests for (3). In such cases, we 7536edb685aSTomi Valkeinen * come in here with FS flag set in the node state from 7546edb685aSTomi Valkeinen * the previous frame since it only gets cleared in 7556edb685aSTomi Valkeinen * cfe_eof_isr(). Handle the FE for the previous 7566edb685aSTomi Valkeinen * frame first before the FS handler for the current 7576edb685aSTomi Valkeinen * frame. 7586edb685aSTomi Valkeinen */ 7596edb685aSTomi Valkeinen if (check_state(cfe, FS_INT, node->id) && 7606edb685aSTomi Valkeinen !check_state(cfe, FE_INT, node->id)) { 7616edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s] Handling missing previous FE interrupt\n", 7626edb685aSTomi Valkeinen __func__, node_desc[node->id].name); 7636edb685aSTomi Valkeinen cfe_eof_isr(node); 7646edb685aSTomi Valkeinen } 7656edb685aSTomi Valkeinen 7666edb685aSTomi Valkeinen cfe_sof_isr(node); 7676edb685aSTomi Valkeinen } 7686edb685aSTomi Valkeinen 7696edb685aSTomi Valkeinen if (!cfe->job_queued && cfe->job_ready) 7706edb685aSTomi Valkeinen cfe_prepare_next_job(cfe); 7716edb685aSTomi Valkeinen } 7726edb685aSTomi Valkeinen 7736edb685aSTomi Valkeinen spin_unlock(&cfe->state_lock); 7746edb685aSTomi Valkeinen 7756edb685aSTomi Valkeinen return IRQ_HANDLED; 7766edb685aSTomi Valkeinen } 7776edb685aSTomi Valkeinen 7786edb685aSTomi Valkeinen /* 7796edb685aSTomi Valkeinen * Stream helpers 7806edb685aSTomi Valkeinen */ 7816edb685aSTomi Valkeinen 7826edb685aSTomi Valkeinen static int cfe_get_vc_dt_fallback(struct cfe_device *cfe, u8 *vc, u8 *dt) 7836edb685aSTomi Valkeinen { 7846edb685aSTomi Valkeinen struct v4l2_subdev_state *state; 7856edb685aSTomi Valkeinen struct v4l2_mbus_framefmt *fmt; 7866edb685aSTomi Valkeinen const struct cfe_fmt *cfe_fmt; 7876edb685aSTomi Valkeinen 7886edb685aSTomi Valkeinen state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); 7896edb685aSTomi Valkeinen 7906edb685aSTomi Valkeinen fmt = v4l2_subdev_state_get_format(state, CSI2_PAD_SINK, 0); 7916edb685aSTomi Valkeinen if (!fmt) 7926edb685aSTomi Valkeinen return -EINVAL; 7936edb685aSTomi Valkeinen 7946edb685aSTomi Valkeinen cfe_fmt = find_format_by_code(fmt->code); 7956edb685aSTomi Valkeinen if (!cfe_fmt) 7966edb685aSTomi Valkeinen return -EINVAL; 7976edb685aSTomi Valkeinen 7986edb685aSTomi Valkeinen *vc = 0; 7996edb685aSTomi Valkeinen *dt = cfe_fmt->csi_dt; 8006edb685aSTomi Valkeinen 8016edb685aSTomi Valkeinen return 0; 8026edb685aSTomi Valkeinen } 8036edb685aSTomi Valkeinen 8046edb685aSTomi Valkeinen static int cfe_get_vc_dt(struct cfe_device *cfe, unsigned int channel, u8 *vc, 8056edb685aSTomi Valkeinen u8 *dt) 8066edb685aSTomi Valkeinen { 8076edb685aSTomi Valkeinen struct v4l2_mbus_frame_desc remote_desc; 8086edb685aSTomi Valkeinen struct v4l2_subdev_state *state; 8096edb685aSTomi Valkeinen u32 sink_stream; 8106edb685aSTomi Valkeinen unsigned int i; 8116edb685aSTomi Valkeinen int ret; 8126edb685aSTomi Valkeinen 8136edb685aSTomi Valkeinen state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); 8146edb685aSTomi Valkeinen 8156edb685aSTomi Valkeinen ret = v4l2_subdev_routing_find_opposite_end(&state->routing, 8166edb685aSTomi Valkeinen CSI2_PAD_FIRST_SOURCE + channel, 0, NULL, &sink_stream); 8176edb685aSTomi Valkeinen if (ret) 8186edb685aSTomi Valkeinen return ret; 8196edb685aSTomi Valkeinen 8206edb685aSTomi Valkeinen ret = v4l2_subdev_call(cfe->source_sd, pad, get_frame_desc, 8216edb685aSTomi Valkeinen cfe->source_pad, &remote_desc); 8226edb685aSTomi Valkeinen if (ret == -ENOIOCTLCMD) { 8236edb685aSTomi Valkeinen cfe_dbg(cfe, "source does not support get_frame_desc, use fallback\n"); 8246edb685aSTomi Valkeinen return cfe_get_vc_dt_fallback(cfe, vc, dt); 8256edb685aSTomi Valkeinen } else if (ret) { 8266edb685aSTomi Valkeinen cfe_err(cfe, "Failed to get frame descriptor\n"); 8276edb685aSTomi Valkeinen return ret; 8286edb685aSTomi Valkeinen } 8296edb685aSTomi Valkeinen 8306edb685aSTomi Valkeinen if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { 8316edb685aSTomi Valkeinen cfe_err(cfe, "Frame descriptor does not describe CSI-2 link"); 8326edb685aSTomi Valkeinen return -EINVAL; 8336edb685aSTomi Valkeinen } 8346edb685aSTomi Valkeinen 8356edb685aSTomi Valkeinen for (i = 0; i < remote_desc.num_entries; i++) { 8366edb685aSTomi Valkeinen if (remote_desc.entry[i].stream == sink_stream) 8376edb685aSTomi Valkeinen break; 8386edb685aSTomi Valkeinen } 8396edb685aSTomi Valkeinen 8406edb685aSTomi Valkeinen if (i == remote_desc.num_entries) { 8416edb685aSTomi Valkeinen cfe_err(cfe, "Stream %u not found in remote frame desc\n", 8426edb685aSTomi Valkeinen sink_stream); 8436edb685aSTomi Valkeinen return -EINVAL; 8446edb685aSTomi Valkeinen } 8456edb685aSTomi Valkeinen 8466edb685aSTomi Valkeinen *vc = remote_desc.entry[i].bus.csi2.vc; 8476edb685aSTomi Valkeinen *dt = remote_desc.entry[i].bus.csi2.dt; 8486edb685aSTomi Valkeinen 8496edb685aSTomi Valkeinen return 0; 8506edb685aSTomi Valkeinen } 8516edb685aSTomi Valkeinen 8526edb685aSTomi Valkeinen static int cfe_start_channel(struct cfe_node *node) 8536edb685aSTomi Valkeinen { 8546edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 8556edb685aSTomi Valkeinen struct v4l2_subdev_state *state; 8566edb685aSTomi Valkeinen struct v4l2_mbus_framefmt *source_fmt; 8576edb685aSTomi Valkeinen const struct cfe_fmt *fmt; 8586edb685aSTomi Valkeinen unsigned long flags; 8596edb685aSTomi Valkeinen bool start_fe; 8606edb685aSTomi Valkeinen int ret; 8616edb685aSTomi Valkeinen 8626edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); 8636edb685aSTomi Valkeinen 8646edb685aSTomi Valkeinen start_fe = is_fe_enabled(cfe) && 8656edb685aSTomi Valkeinen test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); 8666edb685aSTomi Valkeinen 8676edb685aSTomi Valkeinen state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); 8686edb685aSTomi Valkeinen 8696edb685aSTomi Valkeinen if (start_fe) { 8706edb685aSTomi Valkeinen unsigned int width, height; 8716edb685aSTomi Valkeinen u8 vc, dt; 8726edb685aSTomi Valkeinen 8736edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: %s using csi2 channel %d\n", __func__, 8746edb685aSTomi Valkeinen node_desc[FE_OUT0].name, cfe->fe_csi2_channel); 8756edb685aSTomi Valkeinen 8766edb685aSTomi Valkeinen ret = cfe_get_vc_dt(cfe, cfe->fe_csi2_channel, &vc, &dt); 8776edb685aSTomi Valkeinen if (ret) 8786edb685aSTomi Valkeinen return ret; 8796edb685aSTomi Valkeinen 8806edb685aSTomi Valkeinen source_fmt = v4l2_subdev_state_get_format(state, 8816edb685aSTomi Valkeinen node_desc[cfe->fe_csi2_channel].link_pad); 8826edb685aSTomi Valkeinen fmt = find_format_by_code(source_fmt->code); 8836edb685aSTomi Valkeinen 8846edb685aSTomi Valkeinen width = source_fmt->width; 8856edb685aSTomi Valkeinen height = source_fmt->height; 8866edb685aSTomi Valkeinen 8876edb685aSTomi Valkeinen /* Must have a valid CSI2 datatype. */ 8886edb685aSTomi Valkeinen WARN_ON(!fmt->csi_dt); 8896edb685aSTomi Valkeinen 8906edb685aSTomi Valkeinen /* 8916edb685aSTomi Valkeinen * Start the associated CSI2 Channel as well. 8926edb685aSTomi Valkeinen * 8936edb685aSTomi Valkeinen * Must write to the ADDR register to latch the ctrl values 8946edb685aSTomi Valkeinen * even if we are connected to the front end. Once running, 8956edb685aSTomi Valkeinen * this is handled by the CSI2 AUTO_ARM mode. 8966edb685aSTomi Valkeinen */ 8976edb685aSTomi Valkeinen csi2_start_channel(&cfe->csi2, cfe->fe_csi2_channel, 8986edb685aSTomi Valkeinen CSI2_MODE_FE_STREAMING, 8996edb685aSTomi Valkeinen true, false, width, height, vc, dt); 9006edb685aSTomi Valkeinen csi2_set_buffer(&cfe->csi2, cfe->fe_csi2_channel, 0, 0, -1); 9016edb685aSTomi Valkeinen pisp_fe_start(&cfe->fe); 9026edb685aSTomi Valkeinen } 9036edb685aSTomi Valkeinen 9046edb685aSTomi Valkeinen if (is_csi2_node(node)) { 9056edb685aSTomi Valkeinen unsigned int width = 0, height = 0; 9066edb685aSTomi Valkeinen u8 vc, dt; 9076edb685aSTomi Valkeinen 9086edb685aSTomi Valkeinen ret = cfe_get_vc_dt(cfe, node->id, &vc, &dt); 9096edb685aSTomi Valkeinen if (ret) { 9106edb685aSTomi Valkeinen if (start_fe) { 9116edb685aSTomi Valkeinen csi2_stop_channel(&cfe->csi2, 9126edb685aSTomi Valkeinen cfe->fe_csi2_channel); 9136edb685aSTomi Valkeinen pisp_fe_stop(&cfe->fe); 9146edb685aSTomi Valkeinen } 9156edb685aSTomi Valkeinen 9166edb685aSTomi Valkeinen return ret; 9176edb685aSTomi Valkeinen } 9186edb685aSTomi Valkeinen 9196edb685aSTomi Valkeinen u32 mode = CSI2_MODE_NORMAL; 9206edb685aSTomi Valkeinen 9216edb685aSTomi Valkeinen source_fmt = v4l2_subdev_state_get_format(state, 9226edb685aSTomi Valkeinen node_desc[node->id].link_pad); 9236edb685aSTomi Valkeinen fmt = find_format_by_code(source_fmt->code); 9246edb685aSTomi Valkeinen 9256edb685aSTomi Valkeinen /* Must have a valid CSI2 datatype. */ 9266edb685aSTomi Valkeinen WARN_ON(!fmt->csi_dt); 9276edb685aSTomi Valkeinen 9286edb685aSTomi Valkeinen if (is_image_output_node(node)) { 9296edb685aSTomi Valkeinen u32 pixfmt; 9306edb685aSTomi Valkeinen 9316edb685aSTomi Valkeinen width = source_fmt->width; 9326edb685aSTomi Valkeinen height = source_fmt->height; 9336edb685aSTomi Valkeinen 9346edb685aSTomi Valkeinen pixfmt = node->vid_fmt.fmt.pix.pixelformat; 9356edb685aSTomi Valkeinen 9366edb685aSTomi Valkeinen if (pixfmt == fmt->remap[CFE_REMAP_16BIT]) { 9376edb685aSTomi Valkeinen mode = CSI2_MODE_REMAP; 9386edb685aSTomi Valkeinen } else if (pixfmt == fmt->remap[CFE_REMAP_COMPRESSED]) { 9396edb685aSTomi Valkeinen mode = CSI2_MODE_COMPRESSED; 9406edb685aSTomi Valkeinen csi2_set_compression(&cfe->csi2, node->id, 9416edb685aSTomi Valkeinen CSI2_COMPRESSION_DELTA, 0, 9426edb685aSTomi Valkeinen 0); 9436edb685aSTomi Valkeinen } 9446edb685aSTomi Valkeinen } 9456edb685aSTomi Valkeinen /* Unconditionally start this CSI2 channel. */ 9466edb685aSTomi Valkeinen csi2_start_channel(&cfe->csi2, node->id, 9476edb685aSTomi Valkeinen mode, 9486edb685aSTomi Valkeinen /* Auto arm */ 9496edb685aSTomi Valkeinen false, 9506edb685aSTomi Valkeinen /* Pack bytes */ 9516edb685aSTomi Valkeinen is_meta_node(node) ? true : false, 9526edb685aSTomi Valkeinen width, height, vc, dt); 9536edb685aSTomi Valkeinen } 9546edb685aSTomi Valkeinen 9556edb685aSTomi Valkeinen spin_lock_irqsave(&cfe->state_lock, flags); 9566edb685aSTomi Valkeinen if (cfe->job_ready && test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) 9576edb685aSTomi Valkeinen cfe_prepare_next_job(cfe); 9586edb685aSTomi Valkeinen spin_unlock_irqrestore(&cfe->state_lock, flags); 9596edb685aSTomi Valkeinen 9606edb685aSTomi Valkeinen return 0; 9616edb685aSTomi Valkeinen } 9626edb685aSTomi Valkeinen 9636edb685aSTomi Valkeinen static void cfe_stop_channel(struct cfe_node *node, bool fe_stop) 9646edb685aSTomi Valkeinen { 9656edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 9666edb685aSTomi Valkeinen 9676edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s] fe_stop %u\n", __func__, 9686edb685aSTomi Valkeinen node_desc[node->id].name, fe_stop); 9696edb685aSTomi Valkeinen 9706edb685aSTomi Valkeinen if (fe_stop) { 9716edb685aSTomi Valkeinen csi2_stop_channel(&cfe->csi2, cfe->fe_csi2_channel); 9726edb685aSTomi Valkeinen pisp_fe_stop(&cfe->fe); 9736edb685aSTomi Valkeinen } 9746edb685aSTomi Valkeinen 9756edb685aSTomi Valkeinen if (is_csi2_node(node)) 9766edb685aSTomi Valkeinen csi2_stop_channel(&cfe->csi2, node->id); 9776edb685aSTomi Valkeinen } 9786edb685aSTomi Valkeinen 9796edb685aSTomi Valkeinen static void cfe_return_buffers(struct cfe_node *node, 9806edb685aSTomi Valkeinen enum vb2_buffer_state state) 9816edb685aSTomi Valkeinen { 9826edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 9836edb685aSTomi Valkeinen struct cfe_buffer *buf, *tmp; 9846edb685aSTomi Valkeinen unsigned long flags; 9856edb685aSTomi Valkeinen 9866edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); 9876edb685aSTomi Valkeinen 9886edb685aSTomi Valkeinen spin_lock_irqsave(&cfe->state_lock, flags); 9896edb685aSTomi Valkeinen list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) { 9906edb685aSTomi Valkeinen list_del(&buf->list); 9916edb685aSTomi Valkeinen trace_cfe_return_buffer(node->id, buf->vb.vb2_buf.index, 2); 9926edb685aSTomi Valkeinen vb2_buffer_done(&buf->vb.vb2_buf, state); 9936edb685aSTomi Valkeinen } 9946edb685aSTomi Valkeinen 9956edb685aSTomi Valkeinen if (node->cur_frm) { 9966edb685aSTomi Valkeinen trace_cfe_return_buffer(node->id, 9976edb685aSTomi Valkeinen node->cur_frm->vb.vb2_buf.index, 0); 9986edb685aSTomi Valkeinen vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state); 9996edb685aSTomi Valkeinen } 10006edb685aSTomi Valkeinen if (node->next_frm && node->cur_frm != node->next_frm) { 10016edb685aSTomi Valkeinen trace_cfe_return_buffer(node->id, 10026edb685aSTomi Valkeinen node->next_frm->vb.vb2_buf.index, 1); 10036edb685aSTomi Valkeinen vb2_buffer_done(&node->next_frm->vb.vb2_buf, state); 10046edb685aSTomi Valkeinen } 10056edb685aSTomi Valkeinen 10066edb685aSTomi Valkeinen node->cur_frm = NULL; 10076edb685aSTomi Valkeinen node->next_frm = NULL; 10086edb685aSTomi Valkeinen spin_unlock_irqrestore(&cfe->state_lock, flags); 10096edb685aSTomi Valkeinen } 10106edb685aSTomi Valkeinen 10116edb685aSTomi Valkeinen /* 10126edb685aSTomi Valkeinen * vb2 ops 10136edb685aSTomi Valkeinen */ 10146edb685aSTomi Valkeinen 10156edb685aSTomi Valkeinen static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, 10166edb685aSTomi Valkeinen unsigned int *nplanes, unsigned int sizes[], 10176edb685aSTomi Valkeinen struct device *alloc_devs[]) 10186edb685aSTomi Valkeinen { 10196edb685aSTomi Valkeinen struct cfe_node *node = vb2_get_drv_priv(vq); 10206edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 10216edb685aSTomi Valkeinen unsigned int size = is_image_node(node) ? 10226edb685aSTomi Valkeinen node->vid_fmt.fmt.pix.sizeimage : 10236edb685aSTomi Valkeinen node->meta_fmt.fmt.meta.buffersize; 10246edb685aSTomi Valkeinen 10256edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, 10266edb685aSTomi Valkeinen node->buffer_queue.type); 10276edb685aSTomi Valkeinen 10286edb685aSTomi Valkeinen if (vq->max_num_buffers + *nbuffers < 3) 10296edb685aSTomi Valkeinen *nbuffers = 3 - vq->max_num_buffers; 10306edb685aSTomi Valkeinen 10316edb685aSTomi Valkeinen if (*nplanes) { 10326edb685aSTomi Valkeinen if (sizes[0] < size) { 10336edb685aSTomi Valkeinen cfe_err(cfe, "sizes[0] %i < size %u\n", sizes[0], size); 10346edb685aSTomi Valkeinen return -EINVAL; 10356edb685aSTomi Valkeinen } 10366edb685aSTomi Valkeinen size = sizes[0]; 10376edb685aSTomi Valkeinen } 10386edb685aSTomi Valkeinen 10396edb685aSTomi Valkeinen *nplanes = 1; 10406edb685aSTomi Valkeinen sizes[0] = size; 10416edb685aSTomi Valkeinen 10426edb685aSTomi Valkeinen return 0; 10436edb685aSTomi Valkeinen } 10446edb685aSTomi Valkeinen 10456edb685aSTomi Valkeinen static int cfe_buffer_prepare(struct vb2_buffer *vb) 10466edb685aSTomi Valkeinen { 10476edb685aSTomi Valkeinen struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue); 10486edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 10496edb685aSTomi Valkeinen struct cfe_buffer *buf = to_cfe_buffer(vb); 10506edb685aSTomi Valkeinen unsigned long size; 10516edb685aSTomi Valkeinen 10526edb685aSTomi Valkeinen trace_cfe_buffer_prepare(node->id, vb); 10536edb685aSTomi Valkeinen 10546edb685aSTomi Valkeinen size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage : 10556edb685aSTomi Valkeinen node->meta_fmt.fmt.meta.buffersize; 10566edb685aSTomi Valkeinen if (vb2_plane_size(vb, 0) < size) { 10576edb685aSTomi Valkeinen cfe_err(cfe, "data will not fit into plane (%lu < %lu)\n", 10586edb685aSTomi Valkeinen vb2_plane_size(vb, 0), size); 10596edb685aSTomi Valkeinen return -EINVAL; 10606edb685aSTomi Valkeinen } 10616edb685aSTomi Valkeinen 10626edb685aSTomi Valkeinen vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); 10636edb685aSTomi Valkeinen 10646edb685aSTomi Valkeinen if (node->id == FE_CONFIG) { 10656edb685aSTomi Valkeinen struct cfe_config_buffer *b = to_cfe_config_buffer(buf); 10666edb685aSTomi Valkeinen void *addr = vb2_plane_vaddr(vb, 0); 10676edb685aSTomi Valkeinen 10686edb685aSTomi Valkeinen memcpy(&b->config, addr, sizeof(struct pisp_fe_config)); 10696edb685aSTomi Valkeinen return pisp_fe_validate_config(&cfe->fe, &b->config, 10706edb685aSTomi Valkeinen &cfe->node[FE_OUT0].vid_fmt, 10716edb685aSTomi Valkeinen &cfe->node[FE_OUT1].vid_fmt); 10726edb685aSTomi Valkeinen } 10736edb685aSTomi Valkeinen 10746edb685aSTomi Valkeinen return 0; 10756edb685aSTomi Valkeinen } 10766edb685aSTomi Valkeinen 10776edb685aSTomi Valkeinen static void cfe_buffer_queue(struct vb2_buffer *vb) 10786edb685aSTomi Valkeinen { 10796edb685aSTomi Valkeinen struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue); 10806edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 10816edb685aSTomi Valkeinen struct cfe_buffer *buf = to_cfe_buffer(vb); 10826edb685aSTomi Valkeinen unsigned long flags; 10836edb685aSTomi Valkeinen bool schedule_now; 10846edb685aSTomi Valkeinen 10856edb685aSTomi Valkeinen spin_lock_irqsave(&cfe->state_lock, flags); 10866edb685aSTomi Valkeinen 10876edb685aSTomi Valkeinen list_add_tail(&buf->list, &node->dma_queue); 10886edb685aSTomi Valkeinen 10896edb685aSTomi Valkeinen if (!cfe->job_ready) 10906edb685aSTomi Valkeinen cfe->job_ready = cfe_check_job_ready(cfe); 10916edb685aSTomi Valkeinen 10926edb685aSTomi Valkeinen schedule_now = !cfe->job_queued && cfe->job_ready && 10936edb685aSTomi Valkeinen test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); 10946edb685aSTomi Valkeinen 10956edb685aSTomi Valkeinen trace_cfe_buffer_queue(node->id, vb, schedule_now); 10966edb685aSTomi Valkeinen 10976edb685aSTomi Valkeinen if (schedule_now) 10986edb685aSTomi Valkeinen cfe_prepare_next_job(cfe); 10996edb685aSTomi Valkeinen 11006edb685aSTomi Valkeinen spin_unlock_irqrestore(&cfe->state_lock, flags); 11016edb685aSTomi Valkeinen } 11026edb685aSTomi Valkeinen 11036edb685aSTomi Valkeinen static s64 cfe_get_source_link_freq(struct cfe_device *cfe) 11046edb685aSTomi Valkeinen { 11056edb685aSTomi Valkeinen struct v4l2_subdev_state *state; 11066edb685aSTomi Valkeinen s64 link_freq; 11076edb685aSTomi Valkeinen u32 bpp; 11086edb685aSTomi Valkeinen 11096edb685aSTomi Valkeinen state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd); 11106edb685aSTomi Valkeinen 11116edb685aSTomi Valkeinen /* 11126edb685aSTomi Valkeinen * v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back 11136edb685aSTomi Valkeinen * to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available. 11146edb685aSTomi Valkeinen * 11156edb685aSTomi Valkeinen * With multistream input there is no single pixel rate, and thus we 11166edb685aSTomi Valkeinen * cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which 11176edb685aSTomi Valkeinen * causes v4l2_get_link_freq() to return an error if it falls back to 11186edb685aSTomi Valkeinen * V4L2_CID_PIXEL_RATE. 11196edb685aSTomi Valkeinen */ 11206edb685aSTomi Valkeinen 11216edb685aSTomi Valkeinen if (state->routing.num_routes == 1) { 11226edb685aSTomi Valkeinen struct v4l2_subdev_route *route = &state->routing.routes[0]; 11236edb685aSTomi Valkeinen struct v4l2_mbus_framefmt *source_fmt; 11246edb685aSTomi Valkeinen const struct cfe_fmt *fmt; 11256edb685aSTomi Valkeinen 11266edb685aSTomi Valkeinen source_fmt = v4l2_subdev_state_get_format(state, 11276edb685aSTomi Valkeinen route->sink_pad, 11286edb685aSTomi Valkeinen route->sink_stream); 11296edb685aSTomi Valkeinen 11306edb685aSTomi Valkeinen fmt = find_format_by_code(source_fmt->code); 11316edb685aSTomi Valkeinen if (!fmt) 11326edb685aSTomi Valkeinen return -EINVAL; 11336edb685aSTomi Valkeinen 11346edb685aSTomi Valkeinen bpp = fmt->depth; 11356edb685aSTomi Valkeinen } else { 11366edb685aSTomi Valkeinen bpp = 0; 11376edb685aSTomi Valkeinen } 11386edb685aSTomi Valkeinen 11396edb685aSTomi Valkeinen link_freq = v4l2_get_link_freq(cfe->source_sd->ctrl_handler, bpp, 11406edb685aSTomi Valkeinen 2 * cfe->csi2.dphy.active_lanes); 11416edb685aSTomi Valkeinen if (link_freq < 0) 11426edb685aSTomi Valkeinen cfe_err(cfe, "failed to get link freq for subdev '%s'\n", 11436edb685aSTomi Valkeinen cfe->source_sd->name); 11446edb685aSTomi Valkeinen 11456edb685aSTomi Valkeinen return link_freq; 11466edb685aSTomi Valkeinen } 11476edb685aSTomi Valkeinen 11486edb685aSTomi Valkeinen static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count) 11496edb685aSTomi Valkeinen { 11506edb685aSTomi Valkeinen struct v4l2_mbus_config mbus_config = { 0 }; 11516edb685aSTomi Valkeinen struct cfe_node *node = vb2_get_drv_priv(vq); 11526edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 11536edb685aSTomi Valkeinen struct v4l2_subdev_state *state; 11546edb685aSTomi Valkeinen struct v4l2_subdev_route *route; 11556edb685aSTomi Valkeinen s64 link_freq; 11566edb685aSTomi Valkeinen int ret; 11576edb685aSTomi Valkeinen 11586edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); 11596edb685aSTomi Valkeinen 11606edb685aSTomi Valkeinen if (!check_state(cfe, NODE_ENABLED, node->id)) { 11616edb685aSTomi Valkeinen cfe_err(cfe, "%s node link is not enabled.\n", 11626edb685aSTomi Valkeinen node_desc[node->id].name); 11636edb685aSTomi Valkeinen ret = -EINVAL; 11646edb685aSTomi Valkeinen goto err_streaming; 11656edb685aSTomi Valkeinen } 11666edb685aSTomi Valkeinen 11676edb685aSTomi Valkeinen ret = pm_runtime_resume_and_get(&cfe->pdev->dev); 11686edb685aSTomi Valkeinen if (ret < 0) { 11696edb685aSTomi Valkeinen cfe_err(cfe, "pm_runtime_resume_and_get failed\n"); 11706edb685aSTomi Valkeinen goto err_streaming; 11716edb685aSTomi Valkeinen } 11726edb685aSTomi Valkeinen 11736edb685aSTomi Valkeinen /* When using the Frontend, we must enable the FE_CONFIG node. */ 11746edb685aSTomi Valkeinen if (is_fe_enabled(cfe) && 11756edb685aSTomi Valkeinen !check_state(cfe, NODE_ENABLED, cfe->node[FE_CONFIG].id)) { 11766edb685aSTomi Valkeinen cfe_err(cfe, "FE enabled, but FE_CONFIG node is not\n"); 11776edb685aSTomi Valkeinen ret = -EINVAL; 11786edb685aSTomi Valkeinen goto err_pm_put; 11796edb685aSTomi Valkeinen } 11806edb685aSTomi Valkeinen 11816edb685aSTomi Valkeinen ret = media_pipeline_start(&node->pad, &cfe->pipe); 11826edb685aSTomi Valkeinen if (ret < 0) { 11836edb685aSTomi Valkeinen cfe_err(cfe, "Failed to start media pipeline: %d\n", ret); 11846edb685aSTomi Valkeinen goto err_pm_put; 11856edb685aSTomi Valkeinen } 11866edb685aSTomi Valkeinen 11876edb685aSTomi Valkeinen state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); 11886edb685aSTomi Valkeinen 11896edb685aSTomi Valkeinen clear_state(cfe, FS_INT | FE_INT, node->id); 11906edb685aSTomi Valkeinen set_state(cfe, NODE_STREAMING, node->id); 11916edb685aSTomi Valkeinen node->fs_count = 0; 11926edb685aSTomi Valkeinen 11936edb685aSTomi Valkeinen ret = cfe_start_channel(node); 11946edb685aSTomi Valkeinen if (ret) 11956edb685aSTomi Valkeinen goto err_unlock_state; 11966edb685aSTomi Valkeinen 11976edb685aSTomi Valkeinen if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) { 11986edb685aSTomi Valkeinen cfe_dbg(cfe, "Streaming on hold, as all nodes are not set to streaming yet\n"); 11996edb685aSTomi Valkeinen v4l2_subdev_unlock_state(state); 12006edb685aSTomi Valkeinen return 0; 12016edb685aSTomi Valkeinen } 12026edb685aSTomi Valkeinen 12036edb685aSTomi Valkeinen cfg_reg_write(cfe, MIPICFG_CFG, MIPICFG_CFG_SEL_CSI); 12046edb685aSTomi Valkeinen cfg_reg_write(cfe, MIPICFG_INTE, 12056edb685aSTomi Valkeinen MIPICFG_INT_CSI_DMA | MIPICFG_INT_PISP_FE); 12066edb685aSTomi Valkeinen 12076edb685aSTomi Valkeinen ret = v4l2_subdev_call(cfe->source_sd, pad, get_mbus_config, 0, 12086edb685aSTomi Valkeinen &mbus_config); 12096edb685aSTomi Valkeinen if (ret < 0 && ret != -ENOIOCTLCMD) { 12106edb685aSTomi Valkeinen cfe_err(cfe, "g_mbus_config failed\n"); 12116edb685aSTomi Valkeinen goto err_clear_inte; 12126edb685aSTomi Valkeinen } 12136edb685aSTomi Valkeinen 12146edb685aSTomi Valkeinen cfe->csi2.dphy.active_lanes = mbus_config.bus.mipi_csi2.num_data_lanes; 12156edb685aSTomi Valkeinen if (!cfe->csi2.dphy.active_lanes) 12166edb685aSTomi Valkeinen cfe->csi2.dphy.active_lanes = cfe->csi2.dphy.max_lanes; 12176edb685aSTomi Valkeinen if (cfe->csi2.dphy.active_lanes > cfe->csi2.dphy.max_lanes) { 12186edb685aSTomi Valkeinen cfe_err(cfe, "Device has requested %u data lanes, which is >%u configured in DT\n", 12196edb685aSTomi Valkeinen cfe->csi2.dphy.active_lanes, cfe->csi2.dphy.max_lanes); 12206edb685aSTomi Valkeinen ret = -EINVAL; 12216edb685aSTomi Valkeinen goto err_clear_inte; 12226edb685aSTomi Valkeinen } 12236edb685aSTomi Valkeinen 12246edb685aSTomi Valkeinen link_freq = cfe_get_source_link_freq(cfe); 12256edb685aSTomi Valkeinen if (link_freq < 0) 12266edb685aSTomi Valkeinen goto err_clear_inte; 12276edb685aSTomi Valkeinen 12286edb685aSTomi Valkeinen cfe->csi2.dphy.dphy_rate = div_s64(link_freq * 2, 1000000); 12296edb685aSTomi Valkeinen csi2_open_rx(&cfe->csi2); 12306edb685aSTomi Valkeinen 12316edb685aSTomi Valkeinen cfe->streams_mask = 0; 12326edb685aSTomi Valkeinen 12336edb685aSTomi Valkeinen for_each_active_route(&state->routing, route) 12346edb685aSTomi Valkeinen cfe->streams_mask |= BIT_ULL(route->sink_stream); 12356edb685aSTomi Valkeinen 12366edb685aSTomi Valkeinen ret = v4l2_subdev_enable_streams(cfe->source_sd, cfe->source_pad, 12376edb685aSTomi Valkeinen cfe->streams_mask); 12386edb685aSTomi Valkeinen if (ret) { 12396edb685aSTomi Valkeinen cfe_err(cfe, "stream on failed in subdev\n"); 12406edb685aSTomi Valkeinen goto err_disable_cfe; 12416edb685aSTomi Valkeinen } 12426edb685aSTomi Valkeinen 12436edb685aSTomi Valkeinen cfe_dbg(cfe, "Streaming enabled\n"); 12446edb685aSTomi Valkeinen 12456edb685aSTomi Valkeinen v4l2_subdev_unlock_state(state); 12466edb685aSTomi Valkeinen 12476edb685aSTomi Valkeinen return 0; 12486edb685aSTomi Valkeinen 12496edb685aSTomi Valkeinen err_disable_cfe: 12506edb685aSTomi Valkeinen csi2_close_rx(&cfe->csi2); 12516edb685aSTomi Valkeinen err_clear_inte: 12526edb685aSTomi Valkeinen cfg_reg_write(cfe, MIPICFG_INTE, 0); 12536edb685aSTomi Valkeinen 12546edb685aSTomi Valkeinen cfe_stop_channel(node, 12556edb685aSTomi Valkeinen is_fe_enabled(cfe) && test_all_nodes(cfe, NODE_ENABLED, 12566edb685aSTomi Valkeinen NODE_STREAMING)); 12576edb685aSTomi Valkeinen err_unlock_state: 12586edb685aSTomi Valkeinen v4l2_subdev_unlock_state(state); 12596edb685aSTomi Valkeinen media_pipeline_stop(&node->pad); 12606edb685aSTomi Valkeinen err_pm_put: 12616edb685aSTomi Valkeinen pm_runtime_put(&cfe->pdev->dev); 12626edb685aSTomi Valkeinen err_streaming: 12636edb685aSTomi Valkeinen cfe_return_buffers(node, VB2_BUF_STATE_QUEUED); 12646edb685aSTomi Valkeinen clear_state(cfe, NODE_STREAMING, node->id); 12656edb685aSTomi Valkeinen 12666edb685aSTomi Valkeinen return ret; 12676edb685aSTomi Valkeinen } 12686edb685aSTomi Valkeinen 12696edb685aSTomi Valkeinen static void cfe_stop_streaming(struct vb2_queue *vq) 12706edb685aSTomi Valkeinen { 12716edb685aSTomi Valkeinen struct cfe_node *node = vb2_get_drv_priv(vq); 12726edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 12736edb685aSTomi Valkeinen unsigned long flags; 12746edb685aSTomi Valkeinen bool fe_stop; 12756edb685aSTomi Valkeinen 12766edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); 12776edb685aSTomi Valkeinen 12786edb685aSTomi Valkeinen spin_lock_irqsave(&cfe->state_lock, flags); 12796edb685aSTomi Valkeinen fe_stop = is_fe_enabled(cfe) && 12806edb685aSTomi Valkeinen test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING); 12816edb685aSTomi Valkeinen 12826edb685aSTomi Valkeinen cfe->job_ready = false; 12836edb685aSTomi Valkeinen clear_state(cfe, NODE_STREAMING, node->id); 12846edb685aSTomi Valkeinen spin_unlock_irqrestore(&cfe->state_lock, flags); 12856edb685aSTomi Valkeinen 12866edb685aSTomi Valkeinen cfe_stop_channel(node, fe_stop); 12876edb685aSTomi Valkeinen 12886edb685aSTomi Valkeinen if (!test_any_node(cfe, NODE_STREAMING)) { 12896edb685aSTomi Valkeinen struct v4l2_subdev_state *state; 12906edb685aSTomi Valkeinen int ret; 12916edb685aSTomi Valkeinen 12926edb685aSTomi Valkeinen state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); 12936edb685aSTomi Valkeinen 12946edb685aSTomi Valkeinen ret = v4l2_subdev_disable_streams(cfe->source_sd, 12956edb685aSTomi Valkeinen cfe->source_pad, 12966edb685aSTomi Valkeinen cfe->streams_mask); 12976edb685aSTomi Valkeinen if (ret) 12986edb685aSTomi Valkeinen cfe_err(cfe, "stream disable failed in subdev\n"); 12996edb685aSTomi Valkeinen 13006edb685aSTomi Valkeinen v4l2_subdev_unlock_state(state); 13016edb685aSTomi Valkeinen 13026edb685aSTomi Valkeinen csi2_close_rx(&cfe->csi2); 13036edb685aSTomi Valkeinen 13046edb685aSTomi Valkeinen cfg_reg_write(cfe, MIPICFG_INTE, 0); 13056edb685aSTomi Valkeinen 13066edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: Streaming disabled\n", __func__); 13076edb685aSTomi Valkeinen } 13086edb685aSTomi Valkeinen 13096edb685aSTomi Valkeinen media_pipeline_stop(&node->pad); 13106edb685aSTomi Valkeinen 13116edb685aSTomi Valkeinen /* Clear all queued buffers for the node */ 13126edb685aSTomi Valkeinen cfe_return_buffers(node, VB2_BUF_STATE_ERROR); 13136edb685aSTomi Valkeinen 13146edb685aSTomi Valkeinen pm_runtime_put(&cfe->pdev->dev); 13156edb685aSTomi Valkeinen } 13166edb685aSTomi Valkeinen 13176edb685aSTomi Valkeinen static const struct vb2_ops cfe_video_qops = { 13186edb685aSTomi Valkeinen .wait_prepare = vb2_ops_wait_prepare, 13196edb685aSTomi Valkeinen .wait_finish = vb2_ops_wait_finish, 13206edb685aSTomi Valkeinen .queue_setup = cfe_queue_setup, 13216edb685aSTomi Valkeinen .buf_prepare = cfe_buffer_prepare, 13226edb685aSTomi Valkeinen .buf_queue = cfe_buffer_queue, 13236edb685aSTomi Valkeinen .start_streaming = cfe_start_streaming, 13246edb685aSTomi Valkeinen .stop_streaming = cfe_stop_streaming, 13256edb685aSTomi Valkeinen }; 13266edb685aSTomi Valkeinen 13276edb685aSTomi Valkeinen /* 13286edb685aSTomi Valkeinen * v4l2 ioctl ops 13296edb685aSTomi Valkeinen */ 13306edb685aSTomi Valkeinen 13316edb685aSTomi Valkeinen static int cfe_querycap(struct file *file, void *priv, 13326edb685aSTomi Valkeinen struct v4l2_capability *cap) 13336edb685aSTomi Valkeinen { 13346edb685aSTomi Valkeinen strscpy(cap->driver, CFE_MODULE_NAME, sizeof(cap->driver)); 13356edb685aSTomi Valkeinen strscpy(cap->card, CFE_MODULE_NAME, sizeof(cap->card)); 13366edb685aSTomi Valkeinen 13376edb685aSTomi Valkeinen cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE | 13386edb685aSTomi Valkeinen V4L2_CAP_META_OUTPUT; 13396edb685aSTomi Valkeinen 13406edb685aSTomi Valkeinen return 0; 13416edb685aSTomi Valkeinen } 13426edb685aSTomi Valkeinen 13436edb685aSTomi Valkeinen static int cfe_enum_fmt_vid_cap(struct file *file, void *priv, 13446edb685aSTomi Valkeinen struct v4l2_fmtdesc *f) 13456edb685aSTomi Valkeinen { 13466edb685aSTomi Valkeinen struct cfe_node *node = video_drvdata(file); 13476edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 13486edb685aSTomi Valkeinen unsigned int i, j; 13496edb685aSTomi Valkeinen 13506edb685aSTomi Valkeinen if (!node_supports_image_output(node)) 13516edb685aSTomi Valkeinen return -EINVAL; 13526edb685aSTomi Valkeinen 13536edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); 13546edb685aSTomi Valkeinen 13556edb685aSTomi Valkeinen for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) { 13566edb685aSTomi Valkeinen if (f->mbus_code && formats[i].code != f->mbus_code) 13576edb685aSTomi Valkeinen continue; 13586edb685aSTomi Valkeinen 13596edb685aSTomi Valkeinen if (formats[i].flags & CFE_FORMAT_FLAG_META_OUT || 13606edb685aSTomi Valkeinen formats[i].flags & CFE_FORMAT_FLAG_META_CAP) 13616edb685aSTomi Valkeinen continue; 13626edb685aSTomi Valkeinen 13636edb685aSTomi Valkeinen if (is_fe_node(node) && 13646edb685aSTomi Valkeinen !(formats[i].flags & CFE_FORMAT_FLAG_FE_OUT)) 13656edb685aSTomi Valkeinen continue; 13666edb685aSTomi Valkeinen 13676edb685aSTomi Valkeinen if (j == f->index) { 13686edb685aSTomi Valkeinen f->pixelformat = formats[i].fourcc; 13696edb685aSTomi Valkeinen f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 13706edb685aSTomi Valkeinen return 0; 13716edb685aSTomi Valkeinen } 13726edb685aSTomi Valkeinen j++; 13736edb685aSTomi Valkeinen } 13746edb685aSTomi Valkeinen 13756edb685aSTomi Valkeinen return -EINVAL; 13766edb685aSTomi Valkeinen } 13776edb685aSTomi Valkeinen 13786edb685aSTomi Valkeinen static int cfe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) 13796edb685aSTomi Valkeinen { 13806edb685aSTomi Valkeinen struct cfe_node *node = video_drvdata(file); 13816edb685aSTomi Valkeinen 13826edb685aSTomi Valkeinen if (!node_supports_image(node)) 13836edb685aSTomi Valkeinen return -EINVAL; 13846edb685aSTomi Valkeinen 13856edb685aSTomi Valkeinen *f = node->vid_fmt; 13866edb685aSTomi Valkeinen 13876edb685aSTomi Valkeinen return 0; 13886edb685aSTomi Valkeinen } 13896edb685aSTomi Valkeinen 13906edb685aSTomi Valkeinen static int cfe_validate_fmt_vid_cap(struct cfe_node *node, 13916edb685aSTomi Valkeinen struct v4l2_format *f) 13926edb685aSTomi Valkeinen { 13936edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 13946edb685aSTomi Valkeinen const struct cfe_fmt *fmt; 13956edb685aSTomi Valkeinen 13966edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s] %ux%u, V4L2 pix %p4cc\n", __func__, 13976edb685aSTomi Valkeinen node_desc[node->id].name, f->fmt.pix.width, f->fmt.pix.height, 13986edb685aSTomi Valkeinen &f->fmt.pix.pixelformat); 13996edb685aSTomi Valkeinen 14006edb685aSTomi Valkeinen if (!node_supports_image_output(node)) 14016edb685aSTomi Valkeinen return -EINVAL; 14026edb685aSTomi Valkeinen 14036edb685aSTomi Valkeinen /* 14046edb685aSTomi Valkeinen * Default to a format that works for both CSI2 and FE. 14056edb685aSTomi Valkeinen */ 14066edb685aSTomi Valkeinen fmt = find_format_by_pix(f->fmt.pix.pixelformat); 14076edb685aSTomi Valkeinen if (!fmt) 14086edb685aSTomi Valkeinen fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10); 14096edb685aSTomi Valkeinen 14106edb685aSTomi Valkeinen f->fmt.pix.pixelformat = fmt->fourcc; 14116edb685aSTomi Valkeinen 14126edb685aSTomi Valkeinen if (is_fe_node(node) && fmt->remap[CFE_REMAP_16BIT]) { 14136edb685aSTomi Valkeinen f->fmt.pix.pixelformat = fmt->remap[CFE_REMAP_16BIT]; 14146edb685aSTomi Valkeinen fmt = find_format_by_pix(f->fmt.pix.pixelformat); 14156edb685aSTomi Valkeinen } 14166edb685aSTomi Valkeinen 14176edb685aSTomi Valkeinen f->fmt.pix.field = V4L2_FIELD_NONE; 14186edb685aSTomi Valkeinen 14196edb685aSTomi Valkeinen cfe_calc_vid_format_size_bpl(cfe, fmt, f); 14206edb685aSTomi Valkeinen 14216edb685aSTomi Valkeinen return 0; 14226edb685aSTomi Valkeinen } 14236edb685aSTomi Valkeinen 14246edb685aSTomi Valkeinen static int cfe_s_fmt_vid_cap(struct file *file, void *priv, 14256edb685aSTomi Valkeinen struct v4l2_format *f) 14266edb685aSTomi Valkeinen { 14276edb685aSTomi Valkeinen struct cfe_node *node = video_drvdata(file); 14286edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 14296edb685aSTomi Valkeinen struct vb2_queue *q = &node->buffer_queue; 14306edb685aSTomi Valkeinen int ret; 14316edb685aSTomi Valkeinen 14326edb685aSTomi Valkeinen if (vb2_is_busy(q)) 14336edb685aSTomi Valkeinen return -EBUSY; 14346edb685aSTomi Valkeinen 14356edb685aSTomi Valkeinen ret = cfe_validate_fmt_vid_cap(node, f); 14366edb685aSTomi Valkeinen if (ret) 14376edb685aSTomi Valkeinen return ret; 14386edb685aSTomi Valkeinen 14396edb685aSTomi Valkeinen node->vid_fmt = *f; 14406edb685aSTomi Valkeinen 14416edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: Set %ux%u, V4L2 pix %p4cc\n", __func__, 14426edb685aSTomi Valkeinen node->vid_fmt.fmt.pix.width, node->vid_fmt.fmt.pix.height, 14436edb685aSTomi Valkeinen &node->vid_fmt.fmt.pix.pixelformat); 14446edb685aSTomi Valkeinen 14456edb685aSTomi Valkeinen return 0; 14466edb685aSTomi Valkeinen } 14476edb685aSTomi Valkeinen 14486edb685aSTomi Valkeinen static int cfe_try_fmt_vid_cap(struct file *file, void *priv, 14496edb685aSTomi Valkeinen struct v4l2_format *f) 14506edb685aSTomi Valkeinen { 14516edb685aSTomi Valkeinen struct cfe_node *node = video_drvdata(file); 14526edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 14536edb685aSTomi Valkeinen 14546edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); 14556edb685aSTomi Valkeinen 14566edb685aSTomi Valkeinen return cfe_validate_fmt_vid_cap(node, f); 14576edb685aSTomi Valkeinen } 14586edb685aSTomi Valkeinen 14596edb685aSTomi Valkeinen static int cfe_enum_fmt_meta(struct file *file, void *priv, 14606edb685aSTomi Valkeinen struct v4l2_fmtdesc *f) 14616edb685aSTomi Valkeinen { 14626edb685aSTomi Valkeinen struct cfe_node *node = video_drvdata(file); 14636edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 14646edb685aSTomi Valkeinen 14656edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); 14666edb685aSTomi Valkeinen 14676edb685aSTomi Valkeinen if (!node_supports_meta(node)) 14686edb685aSTomi Valkeinen return -EINVAL; 14696edb685aSTomi Valkeinen 14706edb685aSTomi Valkeinen switch (node->id) { 14716edb685aSTomi Valkeinen case CSI2_CH0...CSI2_CH3: 14726edb685aSTomi Valkeinen f->flags = V4L2_FMT_FLAG_META_LINE_BASED; 14736edb685aSTomi Valkeinen 14746edb685aSTomi Valkeinen switch (f->index) { 14756edb685aSTomi Valkeinen case 0: 14766edb685aSTomi Valkeinen f->pixelformat = V4L2_META_FMT_GENERIC_8; 14776edb685aSTomi Valkeinen return 0; 14786edb685aSTomi Valkeinen case 1: 14796edb685aSTomi Valkeinen f->pixelformat = V4L2_META_FMT_GENERIC_CSI2_10; 14806edb685aSTomi Valkeinen return 0; 14816edb685aSTomi Valkeinen case 2: 14826edb685aSTomi Valkeinen f->pixelformat = V4L2_META_FMT_GENERIC_CSI2_12; 14836edb685aSTomi Valkeinen return 0; 14846edb685aSTomi Valkeinen default: 14856edb685aSTomi Valkeinen return -EINVAL; 14866edb685aSTomi Valkeinen } 14876edb685aSTomi Valkeinen default: 14886edb685aSTomi Valkeinen break; 14896edb685aSTomi Valkeinen } 14906edb685aSTomi Valkeinen 14916edb685aSTomi Valkeinen if (f->index != 0) 14926edb685aSTomi Valkeinen return -EINVAL; 14936edb685aSTomi Valkeinen 14946edb685aSTomi Valkeinen switch (node->id) { 14956edb685aSTomi Valkeinen case FE_STATS: 14966edb685aSTomi Valkeinen f->pixelformat = V4L2_META_FMT_RPI_FE_STATS; 14976edb685aSTomi Valkeinen return 0; 14986edb685aSTomi Valkeinen case FE_CONFIG: 14996edb685aSTomi Valkeinen f->pixelformat = V4L2_META_FMT_RPI_FE_CFG; 15006edb685aSTomi Valkeinen return 0; 15016edb685aSTomi Valkeinen default: 15026edb685aSTomi Valkeinen return -EINVAL; 15036edb685aSTomi Valkeinen } 15046edb685aSTomi Valkeinen } 15056edb685aSTomi Valkeinen 15066edb685aSTomi Valkeinen static int cfe_validate_fmt_meta(struct cfe_node *node, struct v4l2_format *f) 15076edb685aSTomi Valkeinen { 15086edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 15096edb685aSTomi Valkeinen const struct cfe_fmt *fmt; 15106edb685aSTomi Valkeinen 15116edb685aSTomi Valkeinen switch (node->id) { 15126edb685aSTomi Valkeinen case CSI2_CH0...CSI2_CH3: 15136edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s] %ux%u, V4L2 meta %p4cc\n", __func__, 15146edb685aSTomi Valkeinen node_desc[node->id].name, f->fmt.meta.width, 15156edb685aSTomi Valkeinen f->fmt.meta.height, &f->fmt.meta.dataformat); 15166edb685aSTomi Valkeinen break; 15176edb685aSTomi Valkeinen case FE_STATS: 15186edb685aSTomi Valkeinen case FE_CONFIG: 15196edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s] %u bytes, V4L2 meta %p4cc\n", __func__, 15206edb685aSTomi Valkeinen node_desc[node->id].name, f->fmt.meta.buffersize, 15216edb685aSTomi Valkeinen &f->fmt.meta.dataformat); 15226edb685aSTomi Valkeinen break; 15236edb685aSTomi Valkeinen default: 15246edb685aSTomi Valkeinen return -EINVAL; 15256edb685aSTomi Valkeinen } 15266edb685aSTomi Valkeinen 15276edb685aSTomi Valkeinen if (!node_supports_meta(node)) 15286edb685aSTomi Valkeinen return -EINVAL; 15296edb685aSTomi Valkeinen 15306edb685aSTomi Valkeinen switch (node->id) { 15316edb685aSTomi Valkeinen case CSI2_CH0...CSI2_CH3: 15326edb685aSTomi Valkeinen fmt = find_format_by_pix(f->fmt.meta.dataformat); 15336edb685aSTomi Valkeinen if (!fmt || !(fmt->flags & CFE_FORMAT_FLAG_META_CAP)) 15346edb685aSTomi Valkeinen fmt = find_format_by_pix(V4L2_META_FMT_GENERIC_CSI2_10); 15356edb685aSTomi Valkeinen 15366edb685aSTomi Valkeinen f->fmt.meta.dataformat = fmt->fourcc; 15376edb685aSTomi Valkeinen 15386edb685aSTomi Valkeinen cfe_calc_meta_format_size_bpl(cfe, fmt, f); 15396edb685aSTomi Valkeinen 15406edb685aSTomi Valkeinen return 0; 15416edb685aSTomi Valkeinen case FE_STATS: 15426edb685aSTomi Valkeinen f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_STATS; 15436edb685aSTomi Valkeinen f->fmt.meta.buffersize = sizeof(struct pisp_statistics); 15446edb685aSTomi Valkeinen return 0; 15456edb685aSTomi Valkeinen case FE_CONFIG: 15466edb685aSTomi Valkeinen f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_CFG; 15476edb685aSTomi Valkeinen f->fmt.meta.buffersize = sizeof(struct pisp_fe_config); 15486edb685aSTomi Valkeinen return 0; 15496edb685aSTomi Valkeinen default: 15506edb685aSTomi Valkeinen return -EINVAL; 15516edb685aSTomi Valkeinen } 15526edb685aSTomi Valkeinen } 15536edb685aSTomi Valkeinen 15546edb685aSTomi Valkeinen static int cfe_g_fmt_meta(struct file *file, void *priv, struct v4l2_format *f) 15556edb685aSTomi Valkeinen { 15566edb685aSTomi Valkeinen struct cfe_node *node = video_drvdata(file); 15576edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 15586edb685aSTomi Valkeinen 15596edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); 15606edb685aSTomi Valkeinen 15616edb685aSTomi Valkeinen if (!node_supports_meta(node)) 15626edb685aSTomi Valkeinen return -EINVAL; 15636edb685aSTomi Valkeinen 15646edb685aSTomi Valkeinen *f = node->meta_fmt; 15656edb685aSTomi Valkeinen 15666edb685aSTomi Valkeinen return 0; 15676edb685aSTomi Valkeinen } 15686edb685aSTomi Valkeinen 15696edb685aSTomi Valkeinen static int cfe_s_fmt_meta(struct file *file, void *priv, struct v4l2_format *f) 15706edb685aSTomi Valkeinen { 15716edb685aSTomi Valkeinen struct cfe_node *node = video_drvdata(file); 15726edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 15736edb685aSTomi Valkeinen struct vb2_queue *q = &node->buffer_queue; 15746edb685aSTomi Valkeinen int ret; 15756edb685aSTomi Valkeinen 15766edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); 15776edb685aSTomi Valkeinen 15786edb685aSTomi Valkeinen if (vb2_is_busy(q)) 15796edb685aSTomi Valkeinen return -EBUSY; 15806edb685aSTomi Valkeinen 15816edb685aSTomi Valkeinen if (!node_supports_meta(node)) 15826edb685aSTomi Valkeinen return -EINVAL; 15836edb685aSTomi Valkeinen 15846edb685aSTomi Valkeinen ret = cfe_validate_fmt_meta(node, f); 15856edb685aSTomi Valkeinen if (ret) 15866edb685aSTomi Valkeinen return ret; 15876edb685aSTomi Valkeinen 15886edb685aSTomi Valkeinen node->meta_fmt = *f; 15896edb685aSTomi Valkeinen 15906edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: Set %p4cc\n", __func__, 15916edb685aSTomi Valkeinen &node->meta_fmt.fmt.meta.dataformat); 15926edb685aSTomi Valkeinen 15936edb685aSTomi Valkeinen return 0; 15946edb685aSTomi Valkeinen } 15956edb685aSTomi Valkeinen 15966edb685aSTomi Valkeinen static int cfe_try_fmt_meta(struct file *file, void *priv, 15976edb685aSTomi Valkeinen struct v4l2_format *f) 15986edb685aSTomi Valkeinen { 15996edb685aSTomi Valkeinen struct cfe_node *node = video_drvdata(file); 16006edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 16016edb685aSTomi Valkeinen 16026edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name); 16036edb685aSTomi Valkeinen return cfe_validate_fmt_meta(node, f); 16046edb685aSTomi Valkeinen } 16056edb685aSTomi Valkeinen 16066edb685aSTomi Valkeinen static int cfe_enum_framesizes(struct file *file, void *priv, 16076edb685aSTomi Valkeinen struct v4l2_frmsizeenum *fsize) 16086edb685aSTomi Valkeinen { 16096edb685aSTomi Valkeinen struct cfe_node *node = video_drvdata(file); 16106edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 16116edb685aSTomi Valkeinen const struct cfe_fmt *fmt; 16126edb685aSTomi Valkeinen 16136edb685aSTomi Valkeinen cfe_dbg(cfe, "%s [%s]\n", __func__, node_desc[node->id].name); 16146edb685aSTomi Valkeinen 16156edb685aSTomi Valkeinen if (fsize->index > 0) 16166edb685aSTomi Valkeinen return -EINVAL; 16176edb685aSTomi Valkeinen 16186edb685aSTomi Valkeinen /* check for valid format */ 16196edb685aSTomi Valkeinen fmt = find_format_by_pix(fsize->pixel_format); 16206edb685aSTomi Valkeinen if (!fmt) { 16216edb685aSTomi Valkeinen cfe_dbg(cfe, "Invalid pixel code: %x\n", fsize->pixel_format); 16226edb685aSTomi Valkeinen return -EINVAL; 16236edb685aSTomi Valkeinen } 16246edb685aSTomi Valkeinen 16256edb685aSTomi Valkeinen /* TODO: Do we have limits on the step_width? */ 16266edb685aSTomi Valkeinen 16276edb685aSTomi Valkeinen fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 16286edb685aSTomi Valkeinen fsize->stepwise.min_width = MIN_WIDTH; 16296edb685aSTomi Valkeinen fsize->stepwise.max_width = MAX_WIDTH; 16306edb685aSTomi Valkeinen fsize->stepwise.step_width = 2; 16316edb685aSTomi Valkeinen fsize->stepwise.min_height = MIN_HEIGHT; 16326edb685aSTomi Valkeinen fsize->stepwise.max_height = MAX_HEIGHT; 16336edb685aSTomi Valkeinen fsize->stepwise.step_height = 1; 16346edb685aSTomi Valkeinen 16356edb685aSTomi Valkeinen return 0; 16366edb685aSTomi Valkeinen } 16376edb685aSTomi Valkeinen 16386edb685aSTomi Valkeinen static int cfe_vb2_ioctl_reqbufs(struct file *file, void *priv, 16396edb685aSTomi Valkeinen struct v4l2_requestbuffers *p) 16406edb685aSTomi Valkeinen { 16416edb685aSTomi Valkeinen struct video_device *vdev = video_devdata(file); 16426edb685aSTomi Valkeinen struct cfe_node *node = video_get_drvdata(vdev); 16436edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 16446edb685aSTomi Valkeinen int ret; 16456edb685aSTomi Valkeinen 16466edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, 16476edb685aSTomi Valkeinen p->type); 16486edb685aSTomi Valkeinen 16496edb685aSTomi Valkeinen if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && 16506edb685aSTomi Valkeinen p->type != V4L2_BUF_TYPE_META_CAPTURE && 16516edb685aSTomi Valkeinen p->type != V4L2_BUF_TYPE_META_OUTPUT) 16526edb685aSTomi Valkeinen return -EINVAL; 16536edb685aSTomi Valkeinen 16546edb685aSTomi Valkeinen ret = vb2_queue_change_type(vdev->queue, p->type); 16556edb685aSTomi Valkeinen if (ret) 16566edb685aSTomi Valkeinen return ret; 16576edb685aSTomi Valkeinen 16586edb685aSTomi Valkeinen return vb2_ioctl_reqbufs(file, priv, p); 16596edb685aSTomi Valkeinen } 16606edb685aSTomi Valkeinen 16616edb685aSTomi Valkeinen static int cfe_vb2_ioctl_create_bufs(struct file *file, void *priv, 16626edb685aSTomi Valkeinen struct v4l2_create_buffers *p) 16636edb685aSTomi Valkeinen { 16646edb685aSTomi Valkeinen struct video_device *vdev = video_devdata(file); 16656edb685aSTomi Valkeinen struct cfe_node *node = video_get_drvdata(vdev); 16666edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 16676edb685aSTomi Valkeinen int ret; 16686edb685aSTomi Valkeinen 16696edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, 16706edb685aSTomi Valkeinen p->format.type); 16716edb685aSTomi Valkeinen 16726edb685aSTomi Valkeinen if (p->format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE && 16736edb685aSTomi Valkeinen p->format.type != V4L2_BUF_TYPE_META_CAPTURE && 16746edb685aSTomi Valkeinen p->format.type != V4L2_BUF_TYPE_META_OUTPUT) 16756edb685aSTomi Valkeinen return -EINVAL; 16766edb685aSTomi Valkeinen 16776edb685aSTomi Valkeinen ret = vb2_queue_change_type(vdev->queue, p->format.type); 16786edb685aSTomi Valkeinen if (ret) 16796edb685aSTomi Valkeinen return ret; 16806edb685aSTomi Valkeinen 16816edb685aSTomi Valkeinen return vb2_ioctl_create_bufs(file, priv, p); 16826edb685aSTomi Valkeinen } 16836edb685aSTomi Valkeinen 16846edb685aSTomi Valkeinen static int cfe_subscribe_event(struct v4l2_fh *fh, 16856edb685aSTomi Valkeinen const struct v4l2_event_subscription *sub) 16866edb685aSTomi Valkeinen { 16876edb685aSTomi Valkeinen struct cfe_node *node = video_get_drvdata(fh->vdev); 16886edb685aSTomi Valkeinen 16896edb685aSTomi Valkeinen switch (sub->type) { 16906edb685aSTomi Valkeinen case V4L2_EVENT_FRAME_SYNC: 16916edb685aSTomi Valkeinen if (!node_supports_image_output(node)) 16926edb685aSTomi Valkeinen break; 16936edb685aSTomi Valkeinen 16946edb685aSTomi Valkeinen return v4l2_event_subscribe(fh, sub, 2, NULL); 16956edb685aSTomi Valkeinen case V4L2_EVENT_SOURCE_CHANGE: 16966edb685aSTomi Valkeinen if (!node_supports_image_output(node) && 16976edb685aSTomi Valkeinen !node_supports_meta_output(node)) 16986edb685aSTomi Valkeinen break; 16996edb685aSTomi Valkeinen 17006edb685aSTomi Valkeinen return v4l2_event_subscribe(fh, sub, 4, NULL); 17016edb685aSTomi Valkeinen } 17026edb685aSTomi Valkeinen 17036edb685aSTomi Valkeinen return v4l2_ctrl_subscribe_event(fh, sub); 17046edb685aSTomi Valkeinen } 17056edb685aSTomi Valkeinen 17066edb685aSTomi Valkeinen static const struct v4l2_ioctl_ops cfe_ioctl_ops = { 17076edb685aSTomi Valkeinen .vidioc_querycap = cfe_querycap, 17086edb685aSTomi Valkeinen .vidioc_enum_fmt_vid_cap = cfe_enum_fmt_vid_cap, 17096edb685aSTomi Valkeinen .vidioc_g_fmt_vid_cap = cfe_g_fmt, 17106edb685aSTomi Valkeinen .vidioc_s_fmt_vid_cap = cfe_s_fmt_vid_cap, 17116edb685aSTomi Valkeinen .vidioc_try_fmt_vid_cap = cfe_try_fmt_vid_cap, 17126edb685aSTomi Valkeinen 17136edb685aSTomi Valkeinen .vidioc_enum_fmt_meta_cap = cfe_enum_fmt_meta, 17146edb685aSTomi Valkeinen .vidioc_g_fmt_meta_cap = cfe_g_fmt_meta, 17156edb685aSTomi Valkeinen .vidioc_s_fmt_meta_cap = cfe_s_fmt_meta, 17166edb685aSTomi Valkeinen .vidioc_try_fmt_meta_cap = cfe_try_fmt_meta, 17176edb685aSTomi Valkeinen 17186edb685aSTomi Valkeinen .vidioc_enum_fmt_meta_out = cfe_enum_fmt_meta, 17196edb685aSTomi Valkeinen .vidioc_g_fmt_meta_out = cfe_g_fmt_meta, 17206edb685aSTomi Valkeinen .vidioc_s_fmt_meta_out = cfe_s_fmt_meta, 17216edb685aSTomi Valkeinen .vidioc_try_fmt_meta_out = cfe_try_fmt_meta, 17226edb685aSTomi Valkeinen 17236edb685aSTomi Valkeinen .vidioc_enum_framesizes = cfe_enum_framesizes, 17246edb685aSTomi Valkeinen 17256edb685aSTomi Valkeinen .vidioc_reqbufs = cfe_vb2_ioctl_reqbufs, 17266edb685aSTomi Valkeinen .vidioc_create_bufs = cfe_vb2_ioctl_create_bufs, 17276edb685aSTomi Valkeinen .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 17286edb685aSTomi Valkeinen .vidioc_querybuf = vb2_ioctl_querybuf, 17296edb685aSTomi Valkeinen .vidioc_qbuf = vb2_ioctl_qbuf, 17306edb685aSTomi Valkeinen .vidioc_dqbuf = vb2_ioctl_dqbuf, 17316edb685aSTomi Valkeinen .vidioc_expbuf = vb2_ioctl_expbuf, 17326edb685aSTomi Valkeinen .vidioc_streamon = vb2_ioctl_streamon, 17336edb685aSTomi Valkeinen .vidioc_streamoff = vb2_ioctl_streamoff, 17346edb685aSTomi Valkeinen 17356edb685aSTomi Valkeinen .vidioc_subscribe_event = cfe_subscribe_event, 17366edb685aSTomi Valkeinen .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 17376edb685aSTomi Valkeinen }; 17386edb685aSTomi Valkeinen 17396edb685aSTomi Valkeinen static void cfe_notify(struct v4l2_subdev *sd, unsigned int notification, 17406edb685aSTomi Valkeinen void *arg) 17416edb685aSTomi Valkeinen { 17426edb685aSTomi Valkeinen struct cfe_device *cfe = to_cfe_device(sd->v4l2_dev); 17436edb685aSTomi Valkeinen 17446edb685aSTomi Valkeinen switch (notification) { 17456edb685aSTomi Valkeinen case V4L2_DEVICE_NOTIFY_EVENT: 17466edb685aSTomi Valkeinen for (unsigned int i = 0; i < NUM_NODES; i++) { 17476edb685aSTomi Valkeinen struct cfe_node *node = &cfe->node[i]; 17486edb685aSTomi Valkeinen 17496edb685aSTomi Valkeinen if (check_state(cfe, NODE_REGISTERED, i)) 17506edb685aSTomi Valkeinen continue; 17516edb685aSTomi Valkeinen 17526edb685aSTomi Valkeinen v4l2_event_queue(&node->video_dev, arg); 17536edb685aSTomi Valkeinen } 17546edb685aSTomi Valkeinen break; 17556edb685aSTomi Valkeinen default: 17566edb685aSTomi Valkeinen break; 17576edb685aSTomi Valkeinen } 17586edb685aSTomi Valkeinen } 17596edb685aSTomi Valkeinen 17606edb685aSTomi Valkeinen /* cfe capture driver file operations */ 17616edb685aSTomi Valkeinen static const struct v4l2_file_operations cfe_fops = { 17626edb685aSTomi Valkeinen .owner = THIS_MODULE, 17636edb685aSTomi Valkeinen .open = v4l2_fh_open, 17646edb685aSTomi Valkeinen .release = vb2_fop_release, 17656edb685aSTomi Valkeinen .poll = vb2_fop_poll, 17666edb685aSTomi Valkeinen .unlocked_ioctl = video_ioctl2, 17676edb685aSTomi Valkeinen .mmap = vb2_fop_mmap, 17686edb685aSTomi Valkeinen }; 17696edb685aSTomi Valkeinen 17706edb685aSTomi Valkeinen static int cfe_video_link_validate(struct media_link *link) 17716edb685aSTomi Valkeinen { 17726edb685aSTomi Valkeinen struct video_device *vd = container_of(link->sink->entity, 17736edb685aSTomi Valkeinen struct video_device, entity); 17746edb685aSTomi Valkeinen struct cfe_node *node = container_of(vd, struct cfe_node, video_dev); 17756edb685aSTomi Valkeinen struct cfe_device *cfe = node->cfe; 17766edb685aSTomi Valkeinen struct v4l2_mbus_framefmt *source_fmt; 17776edb685aSTomi Valkeinen struct v4l2_subdev_state *state; 17786edb685aSTomi Valkeinen struct v4l2_subdev *source_sd; 17796edb685aSTomi Valkeinen int ret = 0; 17806edb685aSTomi Valkeinen 17816edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: [%s] link \"%s\":%u -> \"%s\":%u\n", __func__, 17826edb685aSTomi Valkeinen node_desc[node->id].name, 17836edb685aSTomi Valkeinen link->source->entity->name, link->source->index, 17846edb685aSTomi Valkeinen link->sink->entity->name, link->sink->index); 17856edb685aSTomi Valkeinen 17866edb685aSTomi Valkeinen if (!media_entity_remote_source_pad_unique(link->sink->entity)) { 17876edb685aSTomi Valkeinen cfe_err(cfe, "video node %s pad not connected\n", vd->name); 17886edb685aSTomi Valkeinen return -ENOTCONN; 17896edb685aSTomi Valkeinen } 17906edb685aSTomi Valkeinen 17916edb685aSTomi Valkeinen source_sd = media_entity_to_v4l2_subdev(link->source->entity); 17926edb685aSTomi Valkeinen 17936edb685aSTomi Valkeinen state = v4l2_subdev_lock_and_get_active_state(source_sd); 17946edb685aSTomi Valkeinen 17956edb685aSTomi Valkeinen source_fmt = v4l2_subdev_state_get_format(state, link->source->index); 17966edb685aSTomi Valkeinen if (!source_fmt) { 17976edb685aSTomi Valkeinen ret = -EINVAL; 17986edb685aSTomi Valkeinen goto out; 17996edb685aSTomi Valkeinen } 18006edb685aSTomi Valkeinen 18016edb685aSTomi Valkeinen if (is_image_output_node(node)) { 18026edb685aSTomi Valkeinen struct v4l2_pix_format *pix_fmt = &node->vid_fmt.fmt.pix; 18036edb685aSTomi Valkeinen const struct cfe_fmt *fmt; 18046edb685aSTomi Valkeinen 18056edb685aSTomi Valkeinen if (source_fmt->width != pix_fmt->width || 18066edb685aSTomi Valkeinen source_fmt->height != pix_fmt->height) { 18076edb685aSTomi Valkeinen cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n", 18086edb685aSTomi Valkeinen pix_fmt->width, pix_fmt->height, 18096edb685aSTomi Valkeinen source_fmt->width, source_fmt->height); 18106edb685aSTomi Valkeinen ret = -EINVAL; 18116edb685aSTomi Valkeinen goto out; 18126edb685aSTomi Valkeinen } 18136edb685aSTomi Valkeinen 18146edb685aSTomi Valkeinen fmt = find_format_by_code_and_fourcc(source_fmt->code, 18156edb685aSTomi Valkeinen pix_fmt->pixelformat); 18166edb685aSTomi Valkeinen if (!fmt) { 18176edb685aSTomi Valkeinen cfe_err(cfe, "Format mismatch!\n"); 18186edb685aSTomi Valkeinen ret = -EINVAL; 18196edb685aSTomi Valkeinen goto out; 18206edb685aSTomi Valkeinen } 18216edb685aSTomi Valkeinen } else if (is_csi2_node(node) && is_meta_output_node(node)) { 18226edb685aSTomi Valkeinen struct v4l2_meta_format *meta_fmt = &node->meta_fmt.fmt.meta; 18236edb685aSTomi Valkeinen const struct cfe_fmt *fmt; 18246edb685aSTomi Valkeinen 18256edb685aSTomi Valkeinen if (source_fmt->width != meta_fmt->width || 18266edb685aSTomi Valkeinen source_fmt->height != meta_fmt->height) { 18276edb685aSTomi Valkeinen cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n", 18286edb685aSTomi Valkeinen meta_fmt->width, meta_fmt->height, 18296edb685aSTomi Valkeinen source_fmt->width, source_fmt->height); 18306edb685aSTomi Valkeinen ret = -EINVAL; 18316edb685aSTomi Valkeinen goto out; 18326edb685aSTomi Valkeinen } 18336edb685aSTomi Valkeinen 18346edb685aSTomi Valkeinen fmt = find_format_by_code_and_fourcc(source_fmt->code, 18356edb685aSTomi Valkeinen meta_fmt->dataformat); 18366edb685aSTomi Valkeinen if (!fmt) { 18376edb685aSTomi Valkeinen cfe_err(cfe, "Format mismatch!\n"); 18386edb685aSTomi Valkeinen ret = -EINVAL; 18396edb685aSTomi Valkeinen goto out; 18406edb685aSTomi Valkeinen } 18416edb685aSTomi Valkeinen } 18426edb685aSTomi Valkeinen 18436edb685aSTomi Valkeinen out: 18446edb685aSTomi Valkeinen v4l2_subdev_unlock_state(state); 18456edb685aSTomi Valkeinen 18466edb685aSTomi Valkeinen return ret; 18476edb685aSTomi Valkeinen } 18486edb685aSTomi Valkeinen 18496edb685aSTomi Valkeinen static const struct media_entity_operations cfe_media_entity_ops = { 18506edb685aSTomi Valkeinen .link_validate = cfe_video_link_validate, 18516edb685aSTomi Valkeinen }; 18526edb685aSTomi Valkeinen 18536edb685aSTomi Valkeinen static int cfe_video_link_notify(struct media_link *link, u32 flags, 18546edb685aSTomi Valkeinen unsigned int notification) 18556edb685aSTomi Valkeinen { 18566edb685aSTomi Valkeinen struct media_device *mdev = link->graph_obj.mdev; 18576edb685aSTomi Valkeinen struct cfe_device *cfe = container_of(mdev, struct cfe_device, mdev); 18586edb685aSTomi Valkeinen struct media_entity *fe = &cfe->fe.sd.entity; 18596edb685aSTomi Valkeinen struct media_entity *csi2 = &cfe->csi2.sd.entity; 18606edb685aSTomi Valkeinen unsigned long lock_flags; 18616edb685aSTomi Valkeinen 18626edb685aSTomi Valkeinen if (notification != MEDIA_DEV_NOTIFY_POST_LINK_CH) 18636edb685aSTomi Valkeinen return 0; 18646edb685aSTomi Valkeinen 18656edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: %s[%u] -> %s[%u] 0x%x", __func__, 18666edb685aSTomi Valkeinen link->source->entity->name, link->source->index, 18676edb685aSTomi Valkeinen link->sink->entity->name, link->sink->index, flags); 18686edb685aSTomi Valkeinen 18696edb685aSTomi Valkeinen spin_lock_irqsave(&cfe->state_lock, lock_flags); 18706edb685aSTomi Valkeinen 18716edb685aSTomi Valkeinen for (unsigned int i = 0; i < NUM_NODES; i++) { 18726edb685aSTomi Valkeinen if (link->sink->entity != &cfe->node[i].video_dev.entity && 18736edb685aSTomi Valkeinen link->source->entity != &cfe->node[i].video_dev.entity) 18746edb685aSTomi Valkeinen continue; 18756edb685aSTomi Valkeinen 18766edb685aSTomi Valkeinen if (link->flags & MEDIA_LNK_FL_ENABLED) 18776edb685aSTomi Valkeinen set_state(cfe, NODE_ENABLED, i); 18786edb685aSTomi Valkeinen else 18796edb685aSTomi Valkeinen clear_state(cfe, NODE_ENABLED, i); 18806edb685aSTomi Valkeinen 18816edb685aSTomi Valkeinen break; 18826edb685aSTomi Valkeinen } 18836edb685aSTomi Valkeinen 18846edb685aSTomi Valkeinen spin_unlock_irqrestore(&cfe->state_lock, lock_flags); 18856edb685aSTomi Valkeinen 18866edb685aSTomi Valkeinen if (link->source->entity != csi2) 18876edb685aSTomi Valkeinen return 0; 18886edb685aSTomi Valkeinen if (link->sink->entity != fe) 18896edb685aSTomi Valkeinen return 0; 18906edb685aSTomi Valkeinen if (link->sink->index != 0) 18916edb685aSTomi Valkeinen return 0; 18926edb685aSTomi Valkeinen 18936edb685aSTomi Valkeinen cfe->fe_csi2_channel = -1; 18946edb685aSTomi Valkeinen if (link->flags & MEDIA_LNK_FL_ENABLED) { 18956edb685aSTomi Valkeinen if (link->source->index == node_desc[CSI2_CH0].link_pad) 18966edb685aSTomi Valkeinen cfe->fe_csi2_channel = CSI2_CH0; 18976edb685aSTomi Valkeinen else if (link->source->index == node_desc[CSI2_CH1].link_pad) 18986edb685aSTomi Valkeinen cfe->fe_csi2_channel = CSI2_CH1; 18996edb685aSTomi Valkeinen else if (link->source->index == node_desc[CSI2_CH2].link_pad) 19006edb685aSTomi Valkeinen cfe->fe_csi2_channel = CSI2_CH2; 19016edb685aSTomi Valkeinen else if (link->source->index == node_desc[CSI2_CH3].link_pad) 19026edb685aSTomi Valkeinen cfe->fe_csi2_channel = CSI2_CH3; 19036edb685aSTomi Valkeinen } 19046edb685aSTomi Valkeinen 19056edb685aSTomi Valkeinen if (is_fe_enabled(cfe)) 19066edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: Found CSI2:%d -> FE:0 link\n", __func__, 19076edb685aSTomi Valkeinen cfe->fe_csi2_channel); 19086edb685aSTomi Valkeinen else 19096edb685aSTomi Valkeinen cfe_dbg(cfe, "%s: Unable to find CSI2:x -> FE:0 link\n", 19106edb685aSTomi Valkeinen __func__); 19116edb685aSTomi Valkeinen 19126edb685aSTomi Valkeinen return 0; 19136edb685aSTomi Valkeinen } 19146edb685aSTomi Valkeinen 19156edb685aSTomi Valkeinen static const struct media_device_ops cfe_media_device_ops = { 19166edb685aSTomi Valkeinen .link_notify = cfe_video_link_notify, 19176edb685aSTomi Valkeinen }; 19186edb685aSTomi Valkeinen 19196edb685aSTomi Valkeinen static void cfe_release(struct kref *kref) 19206edb685aSTomi Valkeinen { 19216edb685aSTomi Valkeinen struct cfe_device *cfe = container_of(kref, struct cfe_device, kref); 19226edb685aSTomi Valkeinen 19236edb685aSTomi Valkeinen media_device_cleanup(&cfe->mdev); 19246edb685aSTomi Valkeinen 19256edb685aSTomi Valkeinen kfree(cfe); 19266edb685aSTomi Valkeinen } 19276edb685aSTomi Valkeinen 19286edb685aSTomi Valkeinen static void cfe_put(struct cfe_device *cfe) 19296edb685aSTomi Valkeinen { 19306edb685aSTomi Valkeinen kref_put(&cfe->kref, cfe_release); 19316edb685aSTomi Valkeinen } 19326edb685aSTomi Valkeinen 19336edb685aSTomi Valkeinen static void cfe_get(struct cfe_device *cfe) 19346edb685aSTomi Valkeinen { 19356edb685aSTomi Valkeinen kref_get(&cfe->kref); 19366edb685aSTomi Valkeinen } 19376edb685aSTomi Valkeinen 19386edb685aSTomi Valkeinen static void cfe_node_release(struct video_device *vdev) 19396edb685aSTomi Valkeinen { 19406edb685aSTomi Valkeinen struct cfe_node *node = video_get_drvdata(vdev); 19416edb685aSTomi Valkeinen 19426edb685aSTomi Valkeinen cfe_put(node->cfe); 19436edb685aSTomi Valkeinen } 19446edb685aSTomi Valkeinen 19456edb685aSTomi Valkeinen static int cfe_register_node(struct cfe_device *cfe, int id) 19466edb685aSTomi Valkeinen { 19476edb685aSTomi Valkeinen struct video_device *vdev; 19486edb685aSTomi Valkeinen const struct cfe_fmt *fmt; 19496edb685aSTomi Valkeinen struct vb2_queue *q; 19506edb685aSTomi Valkeinen struct cfe_node *node = &cfe->node[id]; 19516edb685aSTomi Valkeinen int ret; 19526edb685aSTomi Valkeinen 19536edb685aSTomi Valkeinen node->cfe = cfe; 19546edb685aSTomi Valkeinen node->id = id; 19556edb685aSTomi Valkeinen 19566edb685aSTomi Valkeinen if (node_supports_image(node)) { 19576edb685aSTomi Valkeinen if (node_supports_image_output(node)) 19586edb685aSTomi Valkeinen node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 19596edb685aSTomi Valkeinen else 19606edb685aSTomi Valkeinen node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 19616edb685aSTomi Valkeinen 19626edb685aSTomi Valkeinen fmt = find_format_by_code(cfe_default_format.code); 19636edb685aSTomi Valkeinen if (!fmt) { 19646edb685aSTomi Valkeinen cfe_err(cfe, "Failed to find format code\n"); 19656edb685aSTomi Valkeinen return -EINVAL; 19666edb685aSTomi Valkeinen } 19676edb685aSTomi Valkeinen 19686edb685aSTomi Valkeinen node->vid_fmt.fmt.pix.pixelformat = fmt->fourcc; 19696edb685aSTomi Valkeinen v4l2_fill_pix_format(&node->vid_fmt.fmt.pix, 19706edb685aSTomi Valkeinen &cfe_default_format); 19716edb685aSTomi Valkeinen 19726edb685aSTomi Valkeinen ret = cfe_validate_fmt_vid_cap(node, &node->vid_fmt); 19736edb685aSTomi Valkeinen if (ret) 19746edb685aSTomi Valkeinen return ret; 19756edb685aSTomi Valkeinen } 19766edb685aSTomi Valkeinen 19776edb685aSTomi Valkeinen if (node_supports_meta(node)) { 19786edb685aSTomi Valkeinen if (node_supports_meta_output(node)) 19796edb685aSTomi Valkeinen node->meta_fmt.type = V4L2_BUF_TYPE_META_CAPTURE; 19806edb685aSTomi Valkeinen else 19816edb685aSTomi Valkeinen node->meta_fmt.type = V4L2_BUF_TYPE_META_OUTPUT; 19826edb685aSTomi Valkeinen 19836edb685aSTomi Valkeinen ret = cfe_validate_fmt_meta(node, &node->meta_fmt); 19846edb685aSTomi Valkeinen if (ret) 19856edb685aSTomi Valkeinen return ret; 19866edb685aSTomi Valkeinen } 19876edb685aSTomi Valkeinen 19886edb685aSTomi Valkeinen mutex_init(&node->lock); 19896edb685aSTomi Valkeinen 19906edb685aSTomi Valkeinen q = &node->buffer_queue; 19916edb685aSTomi Valkeinen q->type = node_supports_image(node) ? node->vid_fmt.type : 19926edb685aSTomi Valkeinen node->meta_fmt.type; 19936edb685aSTomi Valkeinen q->io_modes = VB2_MMAP | VB2_DMABUF; 19946edb685aSTomi Valkeinen q->drv_priv = node; 19956edb685aSTomi Valkeinen q->ops = &cfe_video_qops; 19966edb685aSTomi Valkeinen q->mem_ops = &vb2_dma_contig_memops; 19976edb685aSTomi Valkeinen q->buf_struct_size = id == FE_CONFIG ? sizeof(struct cfe_config_buffer) 19986edb685aSTomi Valkeinen : sizeof(struct cfe_buffer); 19996edb685aSTomi Valkeinen q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 20006edb685aSTomi Valkeinen q->lock = &node->lock; 20016edb685aSTomi Valkeinen q->min_queued_buffers = 1; 20026edb685aSTomi Valkeinen q->dev = &cfe->pdev->dev; 20036edb685aSTomi Valkeinen 20046edb685aSTomi Valkeinen ret = vb2_queue_init(q); 20056edb685aSTomi Valkeinen if (ret) { 20066edb685aSTomi Valkeinen cfe_err(cfe, "vb2_queue_init() failed\n"); 20076edb685aSTomi Valkeinen return ret; 20086edb685aSTomi Valkeinen } 20096edb685aSTomi Valkeinen 20106edb685aSTomi Valkeinen INIT_LIST_HEAD(&node->dma_queue); 20116edb685aSTomi Valkeinen 20126edb685aSTomi Valkeinen vdev = &node->video_dev; 20136edb685aSTomi Valkeinen vdev->release = cfe_node_release; 20146edb685aSTomi Valkeinen vdev->fops = &cfe_fops; 20156edb685aSTomi Valkeinen vdev->ioctl_ops = &cfe_ioctl_ops; 20166edb685aSTomi Valkeinen vdev->entity.ops = &cfe_media_entity_ops; 20176edb685aSTomi Valkeinen vdev->v4l2_dev = &cfe->v4l2_dev; 20186edb685aSTomi Valkeinen vdev->vfl_dir = (node_supports_image_output(node) || 20196edb685aSTomi Valkeinen node_supports_meta_output(node)) ? 20206edb685aSTomi Valkeinen VFL_DIR_RX : 20216edb685aSTomi Valkeinen VFL_DIR_TX; 20226edb685aSTomi Valkeinen vdev->queue = q; 20236edb685aSTomi Valkeinen vdev->lock = &node->lock; 20246edb685aSTomi Valkeinen vdev->device_caps = node_desc[id].caps; 20256edb685aSTomi Valkeinen vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; 20266edb685aSTomi Valkeinen 20276edb685aSTomi Valkeinen /* Define the device names */ 20286edb685aSTomi Valkeinen snprintf(vdev->name, sizeof(vdev->name), "%s-%s", CFE_MODULE_NAME, 20296edb685aSTomi Valkeinen node_desc[id].name); 20306edb685aSTomi Valkeinen 20316edb685aSTomi Valkeinen video_set_drvdata(vdev, node); 20326edb685aSTomi Valkeinen node->pad.flags = node_desc[id].pad_flags; 20336edb685aSTomi Valkeinen media_entity_pads_init(&vdev->entity, 1, &node->pad); 20346edb685aSTomi Valkeinen 20356edb685aSTomi Valkeinen if (!node_supports_image(node)) { 20366edb685aSTomi Valkeinen v4l2_disable_ioctl(&node->video_dev, 20376edb685aSTomi Valkeinen VIDIOC_ENUM_FRAMEINTERVALS); 20386edb685aSTomi Valkeinen v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES); 20396edb685aSTomi Valkeinen } 20406edb685aSTomi Valkeinen 20416edb685aSTomi Valkeinen ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 20426edb685aSTomi Valkeinen if (ret) { 20436edb685aSTomi Valkeinen cfe_err(cfe, "Unable to register video device %s\n", 20446edb685aSTomi Valkeinen vdev->name); 20456edb685aSTomi Valkeinen return ret; 20466edb685aSTomi Valkeinen } 20476edb685aSTomi Valkeinen 20486edb685aSTomi Valkeinen cfe_info(cfe, "Registered [%s] node id %d as /dev/video%u\n", 20496edb685aSTomi Valkeinen vdev->name, id, vdev->num); 20506edb685aSTomi Valkeinen 20516edb685aSTomi Valkeinen /* 20526edb685aSTomi Valkeinen * Acquire a reference to cfe, which will be released when the video 20536edb685aSTomi Valkeinen * device will be unregistered and userspace will have closed all open 20546edb685aSTomi Valkeinen * file handles. 20556edb685aSTomi Valkeinen */ 20566edb685aSTomi Valkeinen cfe_get(cfe); 20576edb685aSTomi Valkeinen set_state(cfe, NODE_REGISTERED, id); 20586edb685aSTomi Valkeinen 20596edb685aSTomi Valkeinen return 0; 20606edb685aSTomi Valkeinen } 20616edb685aSTomi Valkeinen 20626edb685aSTomi Valkeinen static void cfe_unregister_nodes(struct cfe_device *cfe) 20636edb685aSTomi Valkeinen { 20646edb685aSTomi Valkeinen for (unsigned int i = 0; i < NUM_NODES; i++) { 20656edb685aSTomi Valkeinen struct cfe_node *node = &cfe->node[i]; 20666edb685aSTomi Valkeinen 20676edb685aSTomi Valkeinen if (check_state(cfe, NODE_REGISTERED, i)) { 20686edb685aSTomi Valkeinen clear_state(cfe, NODE_REGISTERED, i); 20696edb685aSTomi Valkeinen video_unregister_device(&node->video_dev); 20706edb685aSTomi Valkeinen } 20716edb685aSTomi Valkeinen } 20726edb685aSTomi Valkeinen } 20736edb685aSTomi Valkeinen 20746edb685aSTomi Valkeinen static int cfe_link_node_pads(struct cfe_device *cfe) 20756edb685aSTomi Valkeinen { 20766edb685aSTomi Valkeinen struct media_pad *remote_pad; 20776edb685aSTomi Valkeinen int ret; 20786edb685aSTomi Valkeinen 20796edb685aSTomi Valkeinen /* Source -> CSI2 */ 20806edb685aSTomi Valkeinen 20816edb685aSTomi Valkeinen ret = v4l2_create_fwnode_links_to_pad(cfe->source_sd, 20826edb685aSTomi Valkeinen &cfe->csi2.pad[CSI2_PAD_SINK], 20836edb685aSTomi Valkeinen MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); 20846edb685aSTomi Valkeinen 20856edb685aSTomi Valkeinen if (ret) { 20866edb685aSTomi Valkeinen cfe_err(cfe, "Failed to create links to the source: %d\n", ret); 20876edb685aSTomi Valkeinen return ret; 20886edb685aSTomi Valkeinen } 20896edb685aSTomi Valkeinen 20906edb685aSTomi Valkeinen remote_pad = media_pad_remote_pad_unique(&cfe->csi2.pad[CSI2_PAD_SINK]); 20916edb685aSTomi Valkeinen if (IS_ERR(remote_pad)) { 20926edb685aSTomi Valkeinen ret = PTR_ERR(remote_pad); 20936edb685aSTomi Valkeinen cfe_err(cfe, "Failed to get unique remote source pad: %d\n", 20946edb685aSTomi Valkeinen ret); 20956edb685aSTomi Valkeinen return ret; 20966edb685aSTomi Valkeinen } 20976edb685aSTomi Valkeinen 20986edb685aSTomi Valkeinen cfe->source_pad = remote_pad->index; 20996edb685aSTomi Valkeinen 21006edb685aSTomi Valkeinen for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) { 21016edb685aSTomi Valkeinen struct cfe_node *node = &cfe->node[i]; 21026edb685aSTomi Valkeinen 21036edb685aSTomi Valkeinen if (!check_state(cfe, NODE_REGISTERED, i)) 21046edb685aSTomi Valkeinen continue; 21056edb685aSTomi Valkeinen 21066edb685aSTomi Valkeinen /* CSI2 channel # -> /dev/video# */ 21076edb685aSTomi Valkeinen ret = media_create_pad_link(&cfe->csi2.sd.entity, 21086edb685aSTomi Valkeinen node_desc[i].link_pad, 21096edb685aSTomi Valkeinen &node->video_dev.entity, 0, 0); 21106edb685aSTomi Valkeinen if (ret) 21116edb685aSTomi Valkeinen return ret; 21126edb685aSTomi Valkeinen 21136edb685aSTomi Valkeinen if (node_supports_image(node)) { 21146edb685aSTomi Valkeinen /* CSI2 channel # -> FE Input */ 21156edb685aSTomi Valkeinen ret = media_create_pad_link(&cfe->csi2.sd.entity, 21166edb685aSTomi Valkeinen node_desc[i].link_pad, 21176edb685aSTomi Valkeinen &cfe->fe.sd.entity, 21186edb685aSTomi Valkeinen FE_STREAM_PAD, 0); 21196edb685aSTomi Valkeinen if (ret) 21206edb685aSTomi Valkeinen return ret; 21216edb685aSTomi Valkeinen } 21226edb685aSTomi Valkeinen } 21236edb685aSTomi Valkeinen 21246edb685aSTomi Valkeinen for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) { 21256edb685aSTomi Valkeinen struct cfe_node *node = &cfe->node[i]; 21266edb685aSTomi Valkeinen struct media_entity *src, *dst; 21276edb685aSTomi Valkeinen unsigned int src_pad, dst_pad; 21286edb685aSTomi Valkeinen 21296edb685aSTomi Valkeinen if (node_desc[i].pad_flags & MEDIA_PAD_FL_SINK) { 21306edb685aSTomi Valkeinen /* FE -> /dev/video# */ 21316edb685aSTomi Valkeinen src = &cfe->fe.sd.entity; 21326edb685aSTomi Valkeinen src_pad = node_desc[i].link_pad; 21336edb685aSTomi Valkeinen dst = &node->video_dev.entity; 21346edb685aSTomi Valkeinen dst_pad = 0; 21356edb685aSTomi Valkeinen } else { 21366edb685aSTomi Valkeinen /* /dev/video# -> FE */ 21376edb685aSTomi Valkeinen dst = &cfe->fe.sd.entity; 21386edb685aSTomi Valkeinen dst_pad = node_desc[i].link_pad; 21396edb685aSTomi Valkeinen src = &node->video_dev.entity; 21406edb685aSTomi Valkeinen src_pad = 0; 21416edb685aSTomi Valkeinen } 21426edb685aSTomi Valkeinen 21436edb685aSTomi Valkeinen ret = media_create_pad_link(src, src_pad, dst, dst_pad, 0); 21446edb685aSTomi Valkeinen if (ret) 21456edb685aSTomi Valkeinen return ret; 21466edb685aSTomi Valkeinen } 21476edb685aSTomi Valkeinen 21486edb685aSTomi Valkeinen return 0; 21496edb685aSTomi Valkeinen } 21506edb685aSTomi Valkeinen 21516edb685aSTomi Valkeinen static int cfe_probe_complete(struct cfe_device *cfe) 21526edb685aSTomi Valkeinen { 21536edb685aSTomi Valkeinen int ret; 21546edb685aSTomi Valkeinen 21556edb685aSTomi Valkeinen cfe->v4l2_dev.notify = cfe_notify; 21566edb685aSTomi Valkeinen 21576edb685aSTomi Valkeinen for (unsigned int i = 0; i < NUM_NODES; i++) { 21586edb685aSTomi Valkeinen ret = cfe_register_node(cfe, i); 21596edb685aSTomi Valkeinen if (ret) { 21606edb685aSTomi Valkeinen cfe_err(cfe, "Unable to register video node %u.\n", i); 21616edb685aSTomi Valkeinen goto unregister; 21626edb685aSTomi Valkeinen } 21636edb685aSTomi Valkeinen } 21646edb685aSTomi Valkeinen 21656edb685aSTomi Valkeinen ret = cfe_link_node_pads(cfe); 21666edb685aSTomi Valkeinen if (ret) { 21676edb685aSTomi Valkeinen cfe_err(cfe, "Unable to link node pads.\n"); 21686edb685aSTomi Valkeinen goto unregister; 21696edb685aSTomi Valkeinen } 21706edb685aSTomi Valkeinen 21716edb685aSTomi Valkeinen ret = v4l2_device_register_subdev_nodes(&cfe->v4l2_dev); 21726edb685aSTomi Valkeinen if (ret) { 21736edb685aSTomi Valkeinen cfe_err(cfe, "Unable to register subdev nodes.\n"); 21746edb685aSTomi Valkeinen goto unregister; 21756edb685aSTomi Valkeinen } 21766edb685aSTomi Valkeinen 21776edb685aSTomi Valkeinen return 0; 21786edb685aSTomi Valkeinen 21796edb685aSTomi Valkeinen unregister: 21806edb685aSTomi Valkeinen cfe_unregister_nodes(cfe); 21816edb685aSTomi Valkeinen return ret; 21826edb685aSTomi Valkeinen } 21836edb685aSTomi Valkeinen 21846edb685aSTomi Valkeinen static int cfe_async_bound(struct v4l2_async_notifier *notifier, 21856edb685aSTomi Valkeinen struct v4l2_subdev *subdev, 21866edb685aSTomi Valkeinen struct v4l2_async_connection *asd) 21876edb685aSTomi Valkeinen { 21886edb685aSTomi Valkeinen struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev); 21896edb685aSTomi Valkeinen 21906edb685aSTomi Valkeinen if (cfe->source_sd) { 21916edb685aSTomi Valkeinen cfe_err(cfe, "Rejecting subdev %s (Already set!!)", 21926edb685aSTomi Valkeinen subdev->name); 21936edb685aSTomi Valkeinen return 0; 21946edb685aSTomi Valkeinen } 21956edb685aSTomi Valkeinen 21966edb685aSTomi Valkeinen cfe->source_sd = subdev; 21976edb685aSTomi Valkeinen 21986edb685aSTomi Valkeinen cfe_dbg(cfe, "Using source %s for capture\n", subdev->name); 21996edb685aSTomi Valkeinen 22006edb685aSTomi Valkeinen return 0; 22016edb685aSTomi Valkeinen } 22026edb685aSTomi Valkeinen 22036edb685aSTomi Valkeinen static int cfe_async_complete(struct v4l2_async_notifier *notifier) 22046edb685aSTomi Valkeinen { 22056edb685aSTomi Valkeinen struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev); 22066edb685aSTomi Valkeinen 22076edb685aSTomi Valkeinen return cfe_probe_complete(cfe); 22086edb685aSTomi Valkeinen } 22096edb685aSTomi Valkeinen 22106edb685aSTomi Valkeinen static const struct v4l2_async_notifier_operations cfe_async_ops = { 22116edb685aSTomi Valkeinen .bound = cfe_async_bound, 22126edb685aSTomi Valkeinen .complete = cfe_async_complete, 22136edb685aSTomi Valkeinen }; 22146edb685aSTomi Valkeinen 22156edb685aSTomi Valkeinen static int cfe_register_async_nf(struct cfe_device *cfe) 22166edb685aSTomi Valkeinen { 22176edb685aSTomi Valkeinen struct platform_device *pdev = cfe->pdev; 22186edb685aSTomi Valkeinen struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; 22196edb685aSTomi Valkeinen struct fwnode_handle *local_ep_fwnode; 22206edb685aSTomi Valkeinen struct v4l2_async_connection *asd; 22216edb685aSTomi Valkeinen int ret; 22226edb685aSTomi Valkeinen 22236edb685aSTomi Valkeinen local_ep_fwnode = fwnode_graph_get_endpoint_by_id(pdev->dev.fwnode, 0, 22246edb685aSTomi Valkeinen 0, 0); 22256edb685aSTomi Valkeinen if (!local_ep_fwnode) { 22266edb685aSTomi Valkeinen cfe_err(cfe, "Failed to find local endpoint fwnode\n"); 22276edb685aSTomi Valkeinen return -ENODEV; 22286edb685aSTomi Valkeinen } 22296edb685aSTomi Valkeinen 22306edb685aSTomi Valkeinen /* Parse the local endpoint and validate its configuration. */ 22316edb685aSTomi Valkeinen ret = v4l2_fwnode_endpoint_parse(local_ep_fwnode, &ep); 22326edb685aSTomi Valkeinen if (ret) { 22336edb685aSTomi Valkeinen cfe_err(cfe, "Failed to find remote endpoint fwnode\n"); 22346edb685aSTomi Valkeinen goto err_put_local_fwnode; 22356edb685aSTomi Valkeinen } 22366edb685aSTomi Valkeinen 22376edb685aSTomi Valkeinen for (unsigned int lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes; 22386edb685aSTomi Valkeinen lane++) { 22396edb685aSTomi Valkeinen if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) { 22406edb685aSTomi Valkeinen cfe_err(cfe, "Data lanes reordering not supported\n"); 22416edb685aSTomi Valkeinen ret = -EINVAL; 22426edb685aSTomi Valkeinen goto err_put_local_fwnode; 22436edb685aSTomi Valkeinen } 22446edb685aSTomi Valkeinen } 22456edb685aSTomi Valkeinen 22466edb685aSTomi Valkeinen cfe->csi2.dphy.max_lanes = ep.bus.mipi_csi2.num_data_lanes; 22476edb685aSTomi Valkeinen cfe->csi2.bus_flags = ep.bus.mipi_csi2.flags; 22486edb685aSTomi Valkeinen 22496edb685aSTomi Valkeinen /* Initialize and register the async notifier. */ 22506edb685aSTomi Valkeinen v4l2_async_nf_init(&cfe->notifier, &cfe->v4l2_dev); 22516edb685aSTomi Valkeinen cfe->notifier.ops = &cfe_async_ops; 22526edb685aSTomi Valkeinen 22536edb685aSTomi Valkeinen asd = v4l2_async_nf_add_fwnode_remote(&cfe->notifier, local_ep_fwnode, 22546edb685aSTomi Valkeinen struct v4l2_async_connection); 22556edb685aSTomi Valkeinen if (IS_ERR(asd)) { 22566edb685aSTomi Valkeinen ret = PTR_ERR(asd); 22576edb685aSTomi Valkeinen cfe_err(cfe, "Error adding subdevice: %d\n", ret); 22586edb685aSTomi Valkeinen goto err_put_local_fwnode; 22596edb685aSTomi Valkeinen } 22606edb685aSTomi Valkeinen 22616edb685aSTomi Valkeinen ret = v4l2_async_nf_register(&cfe->notifier); 22626edb685aSTomi Valkeinen if (ret) { 22636edb685aSTomi Valkeinen cfe_err(cfe, "Error registering async notifier: %d\n", ret); 22646edb685aSTomi Valkeinen goto err_nf_cleanup; 22656edb685aSTomi Valkeinen } 22666edb685aSTomi Valkeinen 22676edb685aSTomi Valkeinen fwnode_handle_put(local_ep_fwnode); 22686edb685aSTomi Valkeinen 22696edb685aSTomi Valkeinen return 0; 22706edb685aSTomi Valkeinen 22716edb685aSTomi Valkeinen err_nf_cleanup: 22726edb685aSTomi Valkeinen v4l2_async_nf_cleanup(&cfe->notifier); 22736edb685aSTomi Valkeinen err_put_local_fwnode: 22746edb685aSTomi Valkeinen fwnode_handle_put(local_ep_fwnode); 22756edb685aSTomi Valkeinen 22766edb685aSTomi Valkeinen return ret; 22776edb685aSTomi Valkeinen } 22786edb685aSTomi Valkeinen 22796edb685aSTomi Valkeinen static int cfe_probe(struct platform_device *pdev) 22806edb685aSTomi Valkeinen { 22816edb685aSTomi Valkeinen struct cfe_device *cfe; 22826edb685aSTomi Valkeinen char debugfs_name[32]; 22836edb685aSTomi Valkeinen int ret; 22846edb685aSTomi Valkeinen 22856edb685aSTomi Valkeinen cfe = kzalloc(sizeof(*cfe), GFP_KERNEL); 22866edb685aSTomi Valkeinen if (!cfe) 22876edb685aSTomi Valkeinen return -ENOMEM; 22886edb685aSTomi Valkeinen 22896edb685aSTomi Valkeinen platform_set_drvdata(pdev, cfe); 22906edb685aSTomi Valkeinen 22916edb685aSTomi Valkeinen kref_init(&cfe->kref); 22926edb685aSTomi Valkeinen cfe->pdev = pdev; 22936edb685aSTomi Valkeinen cfe->fe_csi2_channel = -1; 22946edb685aSTomi Valkeinen spin_lock_init(&cfe->state_lock); 22956edb685aSTomi Valkeinen 22966edb685aSTomi Valkeinen cfe->csi2.base = devm_platform_ioremap_resource(pdev, 0); 22976edb685aSTomi Valkeinen if (IS_ERR(cfe->csi2.base)) { 22986edb685aSTomi Valkeinen dev_err(&pdev->dev, "Failed to get dma io block\n"); 22996edb685aSTomi Valkeinen ret = PTR_ERR(cfe->csi2.base); 23006edb685aSTomi Valkeinen goto err_cfe_put; 23016edb685aSTomi Valkeinen } 23026edb685aSTomi Valkeinen 23036edb685aSTomi Valkeinen cfe->csi2.dphy.base = devm_platform_ioremap_resource(pdev, 1); 23046edb685aSTomi Valkeinen if (IS_ERR(cfe->csi2.dphy.base)) { 23056edb685aSTomi Valkeinen dev_err(&pdev->dev, "Failed to get host io block\n"); 23066edb685aSTomi Valkeinen ret = PTR_ERR(cfe->csi2.dphy.base); 23076edb685aSTomi Valkeinen goto err_cfe_put; 23086edb685aSTomi Valkeinen } 23096edb685aSTomi Valkeinen 23106edb685aSTomi Valkeinen cfe->mipi_cfg_base = devm_platform_ioremap_resource(pdev, 2); 23116edb685aSTomi Valkeinen if (IS_ERR(cfe->mipi_cfg_base)) { 23126edb685aSTomi Valkeinen dev_err(&pdev->dev, "Failed to get mipi cfg io block\n"); 23136edb685aSTomi Valkeinen ret = PTR_ERR(cfe->mipi_cfg_base); 23146edb685aSTomi Valkeinen goto err_cfe_put; 23156edb685aSTomi Valkeinen } 23166edb685aSTomi Valkeinen 23176edb685aSTomi Valkeinen cfe->fe.base = devm_platform_ioremap_resource(pdev, 3); 23186edb685aSTomi Valkeinen if (IS_ERR(cfe->fe.base)) { 23196edb685aSTomi Valkeinen dev_err(&pdev->dev, "Failed to get pisp fe io block\n"); 23206edb685aSTomi Valkeinen ret = PTR_ERR(cfe->fe.base); 23216edb685aSTomi Valkeinen goto err_cfe_put; 23226edb685aSTomi Valkeinen } 23236edb685aSTomi Valkeinen 23246edb685aSTomi Valkeinen ret = platform_get_irq(pdev, 0); 23256edb685aSTomi Valkeinen if (ret <= 0) { 23266edb685aSTomi Valkeinen ret = -EINVAL; 23276edb685aSTomi Valkeinen goto err_cfe_put; 23286edb685aSTomi Valkeinen } 23296edb685aSTomi Valkeinen 23306edb685aSTomi Valkeinen ret = devm_request_irq(&pdev->dev, ret, cfe_isr, 0, "rp1-cfe", cfe); 23316edb685aSTomi Valkeinen if (ret) { 23326edb685aSTomi Valkeinen dev_err(&pdev->dev, "Unable to request interrupt\n"); 23336edb685aSTomi Valkeinen ret = -EINVAL; 23346edb685aSTomi Valkeinen goto err_cfe_put; 23356edb685aSTomi Valkeinen } 23366edb685aSTomi Valkeinen 23376edb685aSTomi Valkeinen ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 23386edb685aSTomi Valkeinen if (ret) { 23396edb685aSTomi Valkeinen dev_err(&pdev->dev, "DMA enable failed\n"); 23406edb685aSTomi Valkeinen goto err_cfe_put; 23416edb685aSTomi Valkeinen } 23426edb685aSTomi Valkeinen 2343*be7de823STomi Valkeinen ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, UINT_MAX); 2344*be7de823STomi Valkeinen if (ret) 2345*be7de823STomi Valkeinen goto err_cfe_put; 2346*be7de823STomi Valkeinen 23476edb685aSTomi Valkeinen /* TODO: Enable clock only when running. */ 23486edb685aSTomi Valkeinen cfe->clk = devm_clk_get(&pdev->dev, NULL); 23498e9a03bdSDan Carpenter if (IS_ERR(cfe->clk)) { 23508e9a03bdSDan Carpenter ret = dev_err_probe(&pdev->dev, PTR_ERR(cfe->clk), 23516edb685aSTomi Valkeinen "clock not found\n"); 23528e9a03bdSDan Carpenter goto err_cfe_put; 23538e9a03bdSDan Carpenter } 23546edb685aSTomi Valkeinen 23556edb685aSTomi Valkeinen cfe->mdev.dev = &pdev->dev; 23566edb685aSTomi Valkeinen cfe->mdev.ops = &cfe_media_device_ops; 23576edb685aSTomi Valkeinen strscpy(cfe->mdev.model, CFE_MODULE_NAME, sizeof(cfe->mdev.model)); 23586edb685aSTomi Valkeinen strscpy(cfe->mdev.serial, "", sizeof(cfe->mdev.serial)); 23596edb685aSTomi Valkeinen snprintf(cfe->mdev.bus_info, sizeof(cfe->mdev.bus_info), "platform:%s", 23606edb685aSTomi Valkeinen dev_name(&pdev->dev)); 23616edb685aSTomi Valkeinen 23626edb685aSTomi Valkeinen media_device_init(&cfe->mdev); 23636edb685aSTomi Valkeinen 23646edb685aSTomi Valkeinen cfe->v4l2_dev.mdev = &cfe->mdev; 23656edb685aSTomi Valkeinen 23666edb685aSTomi Valkeinen ret = v4l2_device_register(&pdev->dev, &cfe->v4l2_dev); 23676edb685aSTomi Valkeinen if (ret) { 23686edb685aSTomi Valkeinen cfe_err(cfe, "Unable to register v4l2 device.\n"); 23696edb685aSTomi Valkeinen goto err_cfe_put; 23706edb685aSTomi Valkeinen } 23716edb685aSTomi Valkeinen 23726edb685aSTomi Valkeinen snprintf(debugfs_name, sizeof(debugfs_name), "rp1-cfe:%s", 23736edb685aSTomi Valkeinen dev_name(&pdev->dev)); 23746edb685aSTomi Valkeinen cfe->debugfs = debugfs_create_dir(debugfs_name, NULL); 23756edb685aSTomi Valkeinen debugfs_create_file("regs", 0440, cfe->debugfs, cfe, 23766edb685aSTomi Valkeinen &mipi_cfg_regs_fops); 23776edb685aSTomi Valkeinen 23786edb685aSTomi Valkeinen /* Enable the block power domain */ 23796edb685aSTomi Valkeinen pm_runtime_enable(&pdev->dev); 23806edb685aSTomi Valkeinen 23816edb685aSTomi Valkeinen ret = pm_runtime_resume_and_get(&cfe->pdev->dev); 23826edb685aSTomi Valkeinen if (ret) 23836edb685aSTomi Valkeinen goto err_runtime_disable; 23846edb685aSTomi Valkeinen 23856edb685aSTomi Valkeinen cfe->csi2.v4l2_dev = &cfe->v4l2_dev; 23866edb685aSTomi Valkeinen ret = csi2_init(&cfe->csi2, cfe->debugfs); 23876edb685aSTomi Valkeinen if (ret) { 23886edb685aSTomi Valkeinen cfe_err(cfe, "Failed to init csi2 (%d)\n", ret); 23896edb685aSTomi Valkeinen goto err_runtime_put; 23906edb685aSTomi Valkeinen } 23916edb685aSTomi Valkeinen 23926edb685aSTomi Valkeinen cfe->fe.v4l2_dev = &cfe->v4l2_dev; 23936edb685aSTomi Valkeinen ret = pisp_fe_init(&cfe->fe, cfe->debugfs); 23946edb685aSTomi Valkeinen if (ret) { 23956edb685aSTomi Valkeinen cfe_err(cfe, "Failed to init pisp fe (%d)\n", ret); 23966edb685aSTomi Valkeinen goto err_csi2_uninit; 23976edb685aSTomi Valkeinen } 23986edb685aSTomi Valkeinen 23996edb685aSTomi Valkeinen cfe->mdev.hw_revision = cfe->fe.hw_revision; 24006edb685aSTomi Valkeinen ret = media_device_register(&cfe->mdev); 24016edb685aSTomi Valkeinen if (ret < 0) { 24026edb685aSTomi Valkeinen cfe_err(cfe, "Unable to register media-controller device.\n"); 24036edb685aSTomi Valkeinen goto err_pisp_fe_uninit; 24046edb685aSTomi Valkeinen } 24056edb685aSTomi Valkeinen 24066edb685aSTomi Valkeinen ret = cfe_register_async_nf(cfe); 24076edb685aSTomi Valkeinen if (ret) { 24086edb685aSTomi Valkeinen cfe_err(cfe, "Failed to connect subdevs\n"); 24096edb685aSTomi Valkeinen goto err_media_unregister; 24106edb685aSTomi Valkeinen } 24116edb685aSTomi Valkeinen 24126edb685aSTomi Valkeinen pm_runtime_put(&cfe->pdev->dev); 24136edb685aSTomi Valkeinen 24146edb685aSTomi Valkeinen return 0; 24156edb685aSTomi Valkeinen 24166edb685aSTomi Valkeinen err_media_unregister: 24176edb685aSTomi Valkeinen media_device_unregister(&cfe->mdev); 24186edb685aSTomi Valkeinen err_pisp_fe_uninit: 24196edb685aSTomi Valkeinen pisp_fe_uninit(&cfe->fe); 24206edb685aSTomi Valkeinen err_csi2_uninit: 24216edb685aSTomi Valkeinen csi2_uninit(&cfe->csi2); 24226edb685aSTomi Valkeinen err_runtime_put: 24236edb685aSTomi Valkeinen pm_runtime_put(&cfe->pdev->dev); 24246edb685aSTomi Valkeinen err_runtime_disable: 24256edb685aSTomi Valkeinen pm_runtime_disable(&pdev->dev); 24266edb685aSTomi Valkeinen debugfs_remove(cfe->debugfs); 24276edb685aSTomi Valkeinen v4l2_device_unregister(&cfe->v4l2_dev); 24286edb685aSTomi Valkeinen err_cfe_put: 24296edb685aSTomi Valkeinen cfe_put(cfe); 24306edb685aSTomi Valkeinen 24316edb685aSTomi Valkeinen return ret; 24326edb685aSTomi Valkeinen } 24336edb685aSTomi Valkeinen 24346edb685aSTomi Valkeinen static void cfe_remove(struct platform_device *pdev) 24356edb685aSTomi Valkeinen { 24366edb685aSTomi Valkeinen struct cfe_device *cfe = platform_get_drvdata(pdev); 24376edb685aSTomi Valkeinen 24386edb685aSTomi Valkeinen debugfs_remove(cfe->debugfs); 24396edb685aSTomi Valkeinen 24406edb685aSTomi Valkeinen v4l2_async_nf_unregister(&cfe->notifier); 24416edb685aSTomi Valkeinen v4l2_async_nf_cleanup(&cfe->notifier); 24426edb685aSTomi Valkeinen 24436edb685aSTomi Valkeinen media_device_unregister(&cfe->mdev); 24446edb685aSTomi Valkeinen cfe_unregister_nodes(cfe); 24456edb685aSTomi Valkeinen 24466edb685aSTomi Valkeinen pisp_fe_uninit(&cfe->fe); 24476edb685aSTomi Valkeinen csi2_uninit(&cfe->csi2); 24486edb685aSTomi Valkeinen 24496edb685aSTomi Valkeinen pm_runtime_disable(&pdev->dev); 24506edb685aSTomi Valkeinen 24516edb685aSTomi Valkeinen v4l2_device_unregister(&cfe->v4l2_dev); 24526edb685aSTomi Valkeinen 24536edb685aSTomi Valkeinen cfe_put(cfe); 24546edb685aSTomi Valkeinen } 24556edb685aSTomi Valkeinen 24566edb685aSTomi Valkeinen static int cfe_runtime_suspend(struct device *dev) 24576edb685aSTomi Valkeinen { 24586edb685aSTomi Valkeinen struct platform_device *pdev = to_platform_device(dev); 24596edb685aSTomi Valkeinen struct cfe_device *cfe = platform_get_drvdata(pdev); 24606edb685aSTomi Valkeinen 24616edb685aSTomi Valkeinen clk_disable_unprepare(cfe->clk); 24626edb685aSTomi Valkeinen 24636edb685aSTomi Valkeinen return 0; 24646edb685aSTomi Valkeinen } 24656edb685aSTomi Valkeinen 24666edb685aSTomi Valkeinen static int cfe_runtime_resume(struct device *dev) 24676edb685aSTomi Valkeinen { 24686edb685aSTomi Valkeinen struct platform_device *pdev = to_platform_device(dev); 24696edb685aSTomi Valkeinen struct cfe_device *cfe = platform_get_drvdata(pdev); 24706edb685aSTomi Valkeinen int ret; 24716edb685aSTomi Valkeinen 24726edb685aSTomi Valkeinen ret = clk_prepare_enable(cfe->clk); 24736edb685aSTomi Valkeinen if (ret) { 24746edb685aSTomi Valkeinen dev_err(dev, "Unable to enable clock\n"); 24756edb685aSTomi Valkeinen return ret; 24766edb685aSTomi Valkeinen } 24776edb685aSTomi Valkeinen 24786edb685aSTomi Valkeinen return 0; 24796edb685aSTomi Valkeinen } 24806edb685aSTomi Valkeinen 24816edb685aSTomi Valkeinen static const struct dev_pm_ops cfe_pm_ops = { 24826edb685aSTomi Valkeinen SET_RUNTIME_PM_OPS(cfe_runtime_suspend, cfe_runtime_resume, NULL) 24836edb685aSTomi Valkeinen SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 24846edb685aSTomi Valkeinen pm_runtime_force_resume) 24856edb685aSTomi Valkeinen }; 24866edb685aSTomi Valkeinen 24876edb685aSTomi Valkeinen static const struct of_device_id cfe_of_match[] = { 24886edb685aSTomi Valkeinen { .compatible = "raspberrypi,rp1-cfe" }, 24896edb685aSTomi Valkeinen { /* sentinel */ }, 24906edb685aSTomi Valkeinen }; 24916edb685aSTomi Valkeinen MODULE_DEVICE_TABLE(of, cfe_of_match); 24926edb685aSTomi Valkeinen 24936edb685aSTomi Valkeinen static struct platform_driver cfe_driver = { 24946edb685aSTomi Valkeinen .probe = cfe_probe, 24956edb685aSTomi Valkeinen .remove = cfe_remove, 24966edb685aSTomi Valkeinen .driver = { 24976edb685aSTomi Valkeinen .name = CFE_MODULE_NAME, 24986edb685aSTomi Valkeinen .of_match_table = cfe_of_match, 24996edb685aSTomi Valkeinen .pm = &cfe_pm_ops, 25006edb685aSTomi Valkeinen }, 25016edb685aSTomi Valkeinen }; 25026edb685aSTomi Valkeinen 25036edb685aSTomi Valkeinen module_platform_driver(cfe_driver); 25046edb685aSTomi Valkeinen 25056edb685aSTomi Valkeinen MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>"); 25066edb685aSTomi Valkeinen MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); 25076edb685aSTomi Valkeinen MODULE_DESCRIPTION("Raspberry Pi RP1 Camera Front End driver"); 25086edb685aSTomi Valkeinen MODULE_LICENSE("GPL"); 25096edb685aSTomi Valkeinen MODULE_VERSION(CFE_VERSION); 2510