18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017,2020 Intel Corporation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based partially on Intel IPU4 driver written by 68c2ecf20Sopenharmony_ci * Sakari Ailus <sakari.ailus@linux.intel.com> 78c2ecf20Sopenharmony_ci * Samu Onkalo <samu.onkalo@intel.com> 88c2ecf20Sopenharmony_ci * Jouni Högander <jouni.hogander@intel.com> 98c2ecf20Sopenharmony_ci * Jouni Ukkonen <jouni.ukkonen@intel.com> 108c2ecf20Sopenharmony_ci * Antti Laakso <antti.laakso@intel.com> 118c2ecf20Sopenharmony_ci * et al. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/pfn.h> 208c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 218c2ecf20Sopenharmony_ci#include <linux/property.h> 228c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 248c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 258c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 268c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 278c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 288c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-sg.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "ipu3-cio2.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct ipu3_cio2_fmt { 338c2ecf20Sopenharmony_ci u32 mbus_code; 348c2ecf20Sopenharmony_ci u32 fourcc; 358c2ecf20Sopenharmony_ci u8 mipicode; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * These are raw formats used in Intel's third generation of 408c2ecf20Sopenharmony_ci * Image Processing Unit known as IPU3. 418c2ecf20Sopenharmony_ci * 10bit raw bayer packed, 32 bytes for every 25 pixels, 428c2ecf20Sopenharmony_ci * last LSB 6 bits unused. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_cistatic const struct ipu3_cio2_fmt formats[] = { 458c2ecf20Sopenharmony_ci { /* put default entry at beginning */ 468c2ecf20Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, 478c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_IPU3_SGRBG10, 488c2ecf20Sopenharmony_ci .mipicode = 0x2b, 498c2ecf20Sopenharmony_ci }, { 508c2ecf20Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, 518c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_IPU3_SGBRG10, 528c2ecf20Sopenharmony_ci .mipicode = 0x2b, 538c2ecf20Sopenharmony_ci }, { 548c2ecf20Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, 558c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_IPU3_SBGGR10, 568c2ecf20Sopenharmony_ci .mipicode = 0x2b, 578c2ecf20Sopenharmony_ci }, { 588c2ecf20Sopenharmony_ci .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, 598c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_IPU3_SRGGB10, 608c2ecf20Sopenharmony_ci .mipicode = 0x2b, 618c2ecf20Sopenharmony_ci }, 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * cio2_find_format - lookup color format by fourcc or/and media bus code 668c2ecf20Sopenharmony_ci * @pixelformat: fourcc to match, ignored if null 678c2ecf20Sopenharmony_ci * @mbus_code: media bus code to match, ignored if null 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_cistatic const struct ipu3_cio2_fmt *cio2_find_format(const u32 *pixelformat, 708c2ecf20Sopenharmony_ci const u32 *mbus_code) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci unsigned int i; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats); i++) { 758c2ecf20Sopenharmony_ci if (pixelformat && *pixelformat != formats[i].fourcc) 768c2ecf20Sopenharmony_ci continue; 778c2ecf20Sopenharmony_ci if (mbus_code && *mbus_code != formats[i].mbus_code) 788c2ecf20Sopenharmony_ci continue; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return &formats[i]; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return NULL; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic inline u32 cio2_bytesperline(const unsigned int width) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci /* 898c2ecf20Sopenharmony_ci * 64 bytes for every 50 pixels, the line length 908c2ecf20Sopenharmony_ci * in bytes is multiple of 64 (line end alignment). 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci return DIV_ROUND_UP(width, 50) * 64; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/**************** FBPT operations ****************/ 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void cio2_fbpt_exit_dummy(struct cio2_device *cio2) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci if (cio2->dummy_lop) { 1008c2ecf20Sopenharmony_ci dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE, 1018c2ecf20Sopenharmony_ci cio2->dummy_lop, cio2->dummy_lop_bus_addr); 1028c2ecf20Sopenharmony_ci cio2->dummy_lop = NULL; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci if (cio2->dummy_page) { 1058c2ecf20Sopenharmony_ci dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE, 1068c2ecf20Sopenharmony_ci cio2->dummy_page, cio2->dummy_page_bus_addr); 1078c2ecf20Sopenharmony_ci cio2->dummy_page = NULL; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int cio2_fbpt_init_dummy(struct cio2_device *cio2) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci unsigned int i; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci cio2->dummy_page = dma_alloc_coherent(&cio2->pci_dev->dev, PAGE_SIZE, 1168c2ecf20Sopenharmony_ci &cio2->dummy_page_bus_addr, 1178c2ecf20Sopenharmony_ci GFP_KERNEL); 1188c2ecf20Sopenharmony_ci cio2->dummy_lop = dma_alloc_coherent(&cio2->pci_dev->dev, PAGE_SIZE, 1198c2ecf20Sopenharmony_ci &cio2->dummy_lop_bus_addr, 1208c2ecf20Sopenharmony_ci GFP_KERNEL); 1218c2ecf20Sopenharmony_ci if (!cio2->dummy_page || !cio2->dummy_lop) { 1228c2ecf20Sopenharmony_ci cio2_fbpt_exit_dummy(cio2); 1238c2ecf20Sopenharmony_ci return -ENOMEM; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci /* 1268c2ecf20Sopenharmony_ci * List of Pointers(LOP) contains 1024x32b pointers to 4KB page each 1278c2ecf20Sopenharmony_ci * Initialize each entry to dummy_page bus base address. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci for (i = 0; i < CIO2_LOP_ENTRIES; i++) 1308c2ecf20Sopenharmony_ci cio2->dummy_lop[i] = PFN_DOWN(cio2->dummy_page_bus_addr); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void cio2_fbpt_entry_enable(struct cio2_device *cio2, 1368c2ecf20Sopenharmony_ci struct cio2_fbpt_entry entry[CIO2_MAX_LOPS]) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci /* 1398c2ecf20Sopenharmony_ci * The CPU first initializes some fields in fbpt, then sets 1408c2ecf20Sopenharmony_ci * the VALID bit, this barrier is to ensure that the DMA(device) 1418c2ecf20Sopenharmony_ci * does not see the VALID bit enabled before other fields are 1428c2ecf20Sopenharmony_ci * initialized; otherwise it could lead to havoc. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci dma_wmb(); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* 1478c2ecf20Sopenharmony_ci * Request interrupts for start and completion 1488c2ecf20Sopenharmony_ci * Valid bit is applicable only to 1st entry 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci entry[0].first_entry.ctrl = CIO2_FBPT_CTRL_VALID | 1518c2ecf20Sopenharmony_ci CIO2_FBPT_CTRL_IOC | CIO2_FBPT_CTRL_IOS; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* Initialize fpbt entries to point to dummy frame */ 1558c2ecf20Sopenharmony_cistatic void cio2_fbpt_entry_init_dummy(struct cio2_device *cio2, 1568c2ecf20Sopenharmony_ci struct cio2_fbpt_entry 1578c2ecf20Sopenharmony_ci entry[CIO2_MAX_LOPS]) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci unsigned int i; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci entry[0].first_entry.first_page_offset = 0; 1628c2ecf20Sopenharmony_ci entry[1].second_entry.num_of_pages = CIO2_LOP_ENTRIES * CIO2_MAX_LOPS; 1638c2ecf20Sopenharmony_ci entry[1].second_entry.last_page_available_bytes = PAGE_SIZE - 1; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci for (i = 0; i < CIO2_MAX_LOPS; i++) 1668c2ecf20Sopenharmony_ci entry[i].lop_page_addr = PFN_DOWN(cio2->dummy_lop_bus_addr); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci cio2_fbpt_entry_enable(cio2, entry); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* Initialize fpbt entries to point to a given buffer */ 1728c2ecf20Sopenharmony_cistatic void cio2_fbpt_entry_init_buf(struct cio2_device *cio2, 1738c2ecf20Sopenharmony_ci struct cio2_buffer *b, 1748c2ecf20Sopenharmony_ci struct cio2_fbpt_entry 1758c2ecf20Sopenharmony_ci entry[CIO2_MAX_LOPS]) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct vb2_buffer *vb = &b->vbb.vb2_buf; 1788c2ecf20Sopenharmony_ci unsigned int length = vb->planes[0].length; 1798c2ecf20Sopenharmony_ci int remaining, i; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci entry[0].first_entry.first_page_offset = b->offset; 1828c2ecf20Sopenharmony_ci remaining = length + entry[0].first_entry.first_page_offset; 1838c2ecf20Sopenharmony_ci entry[1].second_entry.num_of_pages = PFN_UP(remaining); 1848c2ecf20Sopenharmony_ci /* 1858c2ecf20Sopenharmony_ci * last_page_available_bytes has the offset of the last byte in the 1868c2ecf20Sopenharmony_ci * last page which is still accessible by DMA. DMA cannot access 1878c2ecf20Sopenharmony_ci * beyond this point. Valid range for this is from 0 to 4095. 1888c2ecf20Sopenharmony_ci * 0 indicates 1st byte in the page is DMA accessible. 1898c2ecf20Sopenharmony_ci * 4095 (PAGE_SIZE - 1) means every single byte in the last page 1908c2ecf20Sopenharmony_ci * is available for DMA transfer. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci entry[1].second_entry.last_page_available_bytes = 1938c2ecf20Sopenharmony_ci (remaining & ~PAGE_MASK) ? 1948c2ecf20Sopenharmony_ci (remaining & ~PAGE_MASK) - 1 : PAGE_SIZE - 1; 1958c2ecf20Sopenharmony_ci /* Fill FBPT */ 1968c2ecf20Sopenharmony_ci remaining = length; 1978c2ecf20Sopenharmony_ci i = 0; 1988c2ecf20Sopenharmony_ci while (remaining > 0) { 1998c2ecf20Sopenharmony_ci entry->lop_page_addr = PFN_DOWN(b->lop_bus_addr[i]); 2008c2ecf20Sopenharmony_ci remaining -= CIO2_LOP_ENTRIES * PAGE_SIZE; 2018c2ecf20Sopenharmony_ci entry++; 2028c2ecf20Sopenharmony_ci i++; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* 2068c2ecf20Sopenharmony_ci * The first not meaningful FBPT entry should point to a valid LOP 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci entry->lop_page_addr = PFN_DOWN(cio2->dummy_lop_bus_addr); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci cio2_fbpt_entry_enable(cio2, entry); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int cio2_fbpt_init(struct cio2_device *cio2, struct cio2_queue *q) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct device *dev = &cio2->pci_dev->dev; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci q->fbpt = dma_alloc_coherent(dev, CIO2_FBPT_SIZE, &q->fbpt_bus_addr, 2188c2ecf20Sopenharmony_ci GFP_KERNEL); 2198c2ecf20Sopenharmony_ci if (!q->fbpt) 2208c2ecf20Sopenharmony_ci return -ENOMEM; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void cio2_fbpt_exit(struct cio2_queue *q, struct device *dev) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci dma_free_coherent(dev, CIO2_FBPT_SIZE, q->fbpt, q->fbpt_bus_addr); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/**************** CSI2 hardware setup ****************/ 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* 2338c2ecf20Sopenharmony_ci * The CSI2 receiver has several parameters affecting 2348c2ecf20Sopenharmony_ci * the receiver timings. These depend on the MIPI bus frequency 2358c2ecf20Sopenharmony_ci * F in Hz (sensor transmitter rate) as follows: 2368c2ecf20Sopenharmony_ci * register value = (A/1e9 + B * UI) / COUNT_ACC 2378c2ecf20Sopenharmony_ci * where 2388c2ecf20Sopenharmony_ci * UI = 1 / (2 * F) in seconds 2398c2ecf20Sopenharmony_ci * COUNT_ACC = counter accuracy in seconds 2408c2ecf20Sopenharmony_ci * For IPU3 COUNT_ACC = 0.0625 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * A and B are coefficients from the table below, 2438c2ecf20Sopenharmony_ci * depending whether the register minimum or maximum value is 2448c2ecf20Sopenharmony_ci * calculated. 2458c2ecf20Sopenharmony_ci * Minimum Maximum 2468c2ecf20Sopenharmony_ci * Clock lane A B A B 2478c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_clane 0 0 38 0 2488c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_clane 95 -8 300 -16 2498c2ecf20Sopenharmony_ci * Data lanes 2508c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_dlane0 0 0 35 4 2518c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_dlane0 85 -2 145 -6 2528c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_dlane1 0 0 35 4 2538c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_dlane1 85 -2 145 -6 2548c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_dlane2 0 0 35 4 2558c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_dlane2 85 -2 145 -6 2568c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_dlane3 0 0 35 4 2578c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_dlane3 85 -2 145 -6 2588c2ecf20Sopenharmony_ci * 2598c2ecf20Sopenharmony_ci * We use the minimum values of both A and B. 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* 2638c2ecf20Sopenharmony_ci * shift for keeping value range suitable for 32-bit integer arithmetic 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci#define LIMIT_SHIFT 8 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic s32 cio2_rx_timing(s32 a, s32 b, s64 freq, int def) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci const u32 accinv = 16; /* invert of counter resolution */ 2708c2ecf20Sopenharmony_ci const u32 uiinv = 500000000; /* 1e9 / 2 */ 2718c2ecf20Sopenharmony_ci s32 r; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci freq >>= LIMIT_SHIFT; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (WARN_ON(freq <= 0 || freq > S32_MAX)) 2768c2ecf20Sopenharmony_ci return def; 2778c2ecf20Sopenharmony_ci /* 2788c2ecf20Sopenharmony_ci * b could be 0, -2 or -8, so |accinv * b| is always 2798c2ecf20Sopenharmony_ci * less than (1 << ds) and thus |r| < 500000000. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci r = accinv * b * (uiinv >> LIMIT_SHIFT); 2828c2ecf20Sopenharmony_ci r = r / (s32)freq; 2838c2ecf20Sopenharmony_ci /* max value of a is 95 */ 2848c2ecf20Sopenharmony_ci r += accinv * a; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return r; 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* Calculate the the delay value for termination enable of clock lane HS Rx */ 2908c2ecf20Sopenharmony_cistatic int cio2_csi2_calc_timing(struct cio2_device *cio2, struct cio2_queue *q, 2918c2ecf20Sopenharmony_ci struct cio2_csi2_timing *timing) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct device *dev = &cio2->pci_dev->dev; 2948c2ecf20Sopenharmony_ci struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ }; 2958c2ecf20Sopenharmony_ci struct v4l2_ctrl *link_freq; 2968c2ecf20Sopenharmony_ci s64 freq; 2978c2ecf20Sopenharmony_ci int r; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (!q->sensor) 3008c2ecf20Sopenharmony_ci return -ENODEV; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci link_freq = v4l2_ctrl_find(q->sensor->ctrl_handler, V4L2_CID_LINK_FREQ); 3038c2ecf20Sopenharmony_ci if (!link_freq) { 3048c2ecf20Sopenharmony_ci dev_err(dev, "failed to find LINK_FREQ\n"); 3058c2ecf20Sopenharmony_ci return -EPIPE; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci qm.index = v4l2_ctrl_g_ctrl(link_freq); 3098c2ecf20Sopenharmony_ci r = v4l2_querymenu(q->sensor->ctrl_handler, &qm); 3108c2ecf20Sopenharmony_ci if (r) { 3118c2ecf20Sopenharmony_ci dev_err(dev, "failed to get menu item\n"); 3128c2ecf20Sopenharmony_ci return r; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (!qm.value) { 3168c2ecf20Sopenharmony_ci dev_err(dev, "error invalid link_freq\n"); 3178c2ecf20Sopenharmony_ci return -EINVAL; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci freq = qm.value; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci timing->clk_termen = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_A, 3228c2ecf20Sopenharmony_ci CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_B, 3238c2ecf20Sopenharmony_ci freq, 3248c2ecf20Sopenharmony_ci CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT); 3258c2ecf20Sopenharmony_ci timing->clk_settle = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_SETTLE_CLANE_A, 3268c2ecf20Sopenharmony_ci CIO2_CSIRX_DLY_CNT_SETTLE_CLANE_B, 3278c2ecf20Sopenharmony_ci freq, 3288c2ecf20Sopenharmony_ci CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT); 3298c2ecf20Sopenharmony_ci timing->dat_termen = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_TERMEN_DLANE_A, 3308c2ecf20Sopenharmony_ci CIO2_CSIRX_DLY_CNT_TERMEN_DLANE_B, 3318c2ecf20Sopenharmony_ci freq, 3328c2ecf20Sopenharmony_ci CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT); 3338c2ecf20Sopenharmony_ci timing->dat_settle = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_SETTLE_DLANE_A, 3348c2ecf20Sopenharmony_ci CIO2_CSIRX_DLY_CNT_SETTLE_DLANE_B, 3358c2ecf20Sopenharmony_ci freq, 3368c2ecf20Sopenharmony_ci CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci dev_dbg(dev, "freq ct value is %d\n", timing->clk_termen); 3398c2ecf20Sopenharmony_ci dev_dbg(dev, "freq cs value is %d\n", timing->clk_settle); 3408c2ecf20Sopenharmony_ci dev_dbg(dev, "freq dt value is %d\n", timing->dat_termen); 3418c2ecf20Sopenharmony_ci dev_dbg(dev, "freq ds value is %d\n", timing->dat_settle); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_ci}; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci static const int NUM_VCS = 4; 3498c2ecf20Sopenharmony_ci static const int SID; /* Stream id */ 3508c2ecf20Sopenharmony_ci static const int ENTRY; 3518c2ecf20Sopenharmony_ci static const int FBPT_WIDTH = DIV_ROUND_UP(CIO2_MAX_LOPS, 3528c2ecf20Sopenharmony_ci CIO2_FBPT_SUBENTRY_UNIT); 3538c2ecf20Sopenharmony_ci const u32 num_buffers1 = CIO2_MAX_BUFFERS - 1; 3548c2ecf20Sopenharmony_ci const struct ipu3_cio2_fmt *fmt; 3558c2ecf20Sopenharmony_ci void __iomem *const base = cio2->base; 3568c2ecf20Sopenharmony_ci u8 lanes, csi2bus = q->csi2.port; 3578c2ecf20Sopenharmony_ci u8 sensor_vc = SENSOR_VIR_CH_DFLT; 3588c2ecf20Sopenharmony_ci struct cio2_csi2_timing timing = { 0 }; 3598c2ecf20Sopenharmony_ci int i, r; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci fmt = cio2_find_format(NULL, &q->subdev_fmt.code); 3628c2ecf20Sopenharmony_ci if (!fmt) 3638c2ecf20Sopenharmony_ci return -EINVAL; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci lanes = q->csi2.lanes; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci r = cio2_csi2_calc_timing(cio2, q, &timing); 3688c2ecf20Sopenharmony_ci if (r) 3698c2ecf20Sopenharmony_ci return r; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci writel(timing.clk_termen, q->csi_rx_base + 3728c2ecf20Sopenharmony_ci CIO2_REG_CSIRX_DLY_CNT_TERMEN(CIO2_CSIRX_DLY_CNT_CLANE_IDX)); 3738c2ecf20Sopenharmony_ci writel(timing.clk_settle, q->csi_rx_base + 3748c2ecf20Sopenharmony_ci CIO2_REG_CSIRX_DLY_CNT_SETTLE(CIO2_CSIRX_DLY_CNT_CLANE_IDX)); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci for (i = 0; i < lanes; i++) { 3778c2ecf20Sopenharmony_ci writel(timing.dat_termen, q->csi_rx_base + 3788c2ecf20Sopenharmony_ci CIO2_REG_CSIRX_DLY_CNT_TERMEN(i)); 3798c2ecf20Sopenharmony_ci writel(timing.dat_settle, q->csi_rx_base + 3808c2ecf20Sopenharmony_ci CIO2_REG_CSIRX_DLY_CNT_SETTLE(i)); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci writel(CIO2_PBM_WMCTRL1_MIN_2CK | 3848c2ecf20Sopenharmony_ci CIO2_PBM_WMCTRL1_MID1_2CK | 3858c2ecf20Sopenharmony_ci CIO2_PBM_WMCTRL1_MID2_2CK, base + CIO2_REG_PBM_WMCTRL1); 3868c2ecf20Sopenharmony_ci writel(CIO2_PBM_WMCTRL2_HWM_2CK << CIO2_PBM_WMCTRL2_HWM_2CK_SHIFT | 3878c2ecf20Sopenharmony_ci CIO2_PBM_WMCTRL2_LWM_2CK << CIO2_PBM_WMCTRL2_LWM_2CK_SHIFT | 3888c2ecf20Sopenharmony_ci CIO2_PBM_WMCTRL2_OBFFWM_2CK << 3898c2ecf20Sopenharmony_ci CIO2_PBM_WMCTRL2_OBFFWM_2CK_SHIFT | 3908c2ecf20Sopenharmony_ci CIO2_PBM_WMCTRL2_TRANSDYN << CIO2_PBM_WMCTRL2_TRANSDYN_SHIFT | 3918c2ecf20Sopenharmony_ci CIO2_PBM_WMCTRL2_OBFF_MEM_EN, base + CIO2_REG_PBM_WMCTRL2); 3928c2ecf20Sopenharmony_ci writel(CIO2_PBM_ARB_CTRL_LANES_DIV << 3938c2ecf20Sopenharmony_ci CIO2_PBM_ARB_CTRL_LANES_DIV_SHIFT | 3948c2ecf20Sopenharmony_ci CIO2_PBM_ARB_CTRL_LE_EN | 3958c2ecf20Sopenharmony_ci CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN << 3968c2ecf20Sopenharmony_ci CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN_SHIFT | 3978c2ecf20Sopenharmony_ci CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP << 3988c2ecf20Sopenharmony_ci CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP_SHIFT, 3998c2ecf20Sopenharmony_ci base + CIO2_REG_PBM_ARB_CTRL); 4008c2ecf20Sopenharmony_ci writel(CIO2_CSIRX_STATUS_DLANE_HS_MASK, 4018c2ecf20Sopenharmony_ci q->csi_rx_base + CIO2_REG_CSIRX_STATUS_DLANE_HS); 4028c2ecf20Sopenharmony_ci writel(CIO2_CSIRX_STATUS_DLANE_LP_MASK, 4038c2ecf20Sopenharmony_ci q->csi_rx_base + CIO2_REG_CSIRX_STATUS_DLANE_LP); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci writel(CIO2_FB_HPLL_FREQ, base + CIO2_REG_FB_HPLL_FREQ); 4068c2ecf20Sopenharmony_ci writel(CIO2_ISCLK_RATIO, base + CIO2_REG_ISCLK_RATIO); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* Configure MIPI backend */ 4098c2ecf20Sopenharmony_ci for (i = 0; i < NUM_VCS; i++) 4108c2ecf20Sopenharmony_ci writel(1, q->csi_rx_base + CIO2_REG_MIPIBE_SP_LUT_ENTRY(i)); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* There are 16 short packet LUT entry */ 4138c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 4148c2ecf20Sopenharmony_ci writel(CIO2_MIPIBE_LP_LUT_ENTRY_DISREGARD, 4158c2ecf20Sopenharmony_ci q->csi_rx_base + CIO2_REG_MIPIBE_LP_LUT_ENTRY(i)); 4168c2ecf20Sopenharmony_ci writel(CIO2_MIPIBE_GLOBAL_LUT_DISREGARD, 4178c2ecf20Sopenharmony_ci q->csi_rx_base + CIO2_REG_MIPIBE_GLOBAL_LUT_DISREGARD); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci writel(CIO2_INT_EN_EXT_IE_MASK, base + CIO2_REG_INT_EN_EXT_IE); 4208c2ecf20Sopenharmony_ci writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK); 4218c2ecf20Sopenharmony_ci writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_ENABLE); 4228c2ecf20Sopenharmony_ci writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_EDGE); 4238c2ecf20Sopenharmony_ci writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_LEVEL_NOT_PULSE); 4248c2ecf20Sopenharmony_ci writel(CIO2_INT_EN_EXT_OE_MASK, base + CIO2_REG_INT_EN_EXT_OE); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci writel(CIO2_REG_INT_EN_IRQ | CIO2_INT_IOC(CIO2_DMA_CHAN) | 4278c2ecf20Sopenharmony_ci CIO2_REG_INT_EN_IOS(CIO2_DMA_CHAN), 4288c2ecf20Sopenharmony_ci base + CIO2_REG_INT_EN); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci writel((CIO2_PXM_PXF_FMT_CFG_BPP_10 | CIO2_PXM_PXF_FMT_CFG_PCK_64B) 4318c2ecf20Sopenharmony_ci << CIO2_PXM_PXF_FMT_CFG_SID0_SHIFT, 4328c2ecf20Sopenharmony_ci base + CIO2_REG_PXM_PXF_FMT_CFG0(csi2bus)); 4338c2ecf20Sopenharmony_ci writel(SID << CIO2_MIPIBE_LP_LUT_ENTRY_SID_SHIFT | 4348c2ecf20Sopenharmony_ci sensor_vc << CIO2_MIPIBE_LP_LUT_ENTRY_VC_SHIFT | 4358c2ecf20Sopenharmony_ci fmt->mipicode << CIO2_MIPIBE_LP_LUT_ENTRY_FORMAT_TYPE_SHIFT, 4368c2ecf20Sopenharmony_ci q->csi_rx_base + CIO2_REG_MIPIBE_LP_LUT_ENTRY(ENTRY)); 4378c2ecf20Sopenharmony_ci writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_COMP_FORMAT(sensor_vc)); 4388c2ecf20Sopenharmony_ci writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_FORCE_RAW8); 4398c2ecf20Sopenharmony_ci writel(0, base + CIO2_REG_PXM_SID2BID0(csi2bus)); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci writel(lanes, q->csi_rx_base + CIO2_REG_CSIRX_NOF_ENABLED_LANES); 4428c2ecf20Sopenharmony_ci writel(CIO2_CGC_PRIM_TGE | 4438c2ecf20Sopenharmony_ci CIO2_CGC_SIDE_TGE | 4448c2ecf20Sopenharmony_ci CIO2_CGC_XOSC_TGE | 4458c2ecf20Sopenharmony_ci CIO2_CGC_D3I3_TGE | 4468c2ecf20Sopenharmony_ci CIO2_CGC_CSI2_INTERFRAME_TGE | 4478c2ecf20Sopenharmony_ci CIO2_CGC_CSI2_PORT_DCGE | 4488c2ecf20Sopenharmony_ci CIO2_CGC_SIDE_DCGE | 4498c2ecf20Sopenharmony_ci CIO2_CGC_PRIM_DCGE | 4508c2ecf20Sopenharmony_ci CIO2_CGC_ROSC_DCGE | 4518c2ecf20Sopenharmony_ci CIO2_CGC_XOSC_DCGE | 4528c2ecf20Sopenharmony_ci CIO2_CGC_CLKGATE_HOLDOFF << CIO2_CGC_CLKGATE_HOLDOFF_SHIFT | 4538c2ecf20Sopenharmony_ci CIO2_CGC_CSI_CLKGATE_HOLDOFF 4548c2ecf20Sopenharmony_ci << CIO2_CGC_CSI_CLKGATE_HOLDOFF_SHIFT, base + CIO2_REG_CGC); 4558c2ecf20Sopenharmony_ci writel(CIO2_LTRCTRL_LTRDYNEN, base + CIO2_REG_LTRCTRL); 4568c2ecf20Sopenharmony_ci writel(CIO2_LTRVAL0_VAL << CIO2_LTRVAL02_VAL_SHIFT | 4578c2ecf20Sopenharmony_ci CIO2_LTRVAL0_SCALE << CIO2_LTRVAL02_SCALE_SHIFT | 4588c2ecf20Sopenharmony_ci CIO2_LTRVAL1_VAL << CIO2_LTRVAL13_VAL_SHIFT | 4598c2ecf20Sopenharmony_ci CIO2_LTRVAL1_SCALE << CIO2_LTRVAL13_SCALE_SHIFT, 4608c2ecf20Sopenharmony_ci base + CIO2_REG_LTRVAL01); 4618c2ecf20Sopenharmony_ci writel(CIO2_LTRVAL2_VAL << CIO2_LTRVAL02_VAL_SHIFT | 4628c2ecf20Sopenharmony_ci CIO2_LTRVAL2_SCALE << CIO2_LTRVAL02_SCALE_SHIFT | 4638c2ecf20Sopenharmony_ci CIO2_LTRVAL3_VAL << CIO2_LTRVAL13_VAL_SHIFT | 4648c2ecf20Sopenharmony_ci CIO2_LTRVAL3_SCALE << CIO2_LTRVAL13_SCALE_SHIFT, 4658c2ecf20Sopenharmony_ci base + CIO2_REG_LTRVAL23); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci for (i = 0; i < CIO2_NUM_DMA_CHAN; i++) { 4688c2ecf20Sopenharmony_ci writel(0, base + CIO2_REG_CDMABA(i)); 4698c2ecf20Sopenharmony_ci writel(0, base + CIO2_REG_CDMAC0(i)); 4708c2ecf20Sopenharmony_ci writel(0, base + CIO2_REG_CDMAC1(i)); 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* Enable DMA */ 4748c2ecf20Sopenharmony_ci writel(PFN_DOWN(q->fbpt_bus_addr), base + CIO2_REG_CDMABA(CIO2_DMA_CHAN)); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci writel(num_buffers1 << CIO2_CDMAC0_FBPT_LEN_SHIFT | 4778c2ecf20Sopenharmony_ci FBPT_WIDTH << CIO2_CDMAC0_FBPT_WIDTH_SHIFT | 4788c2ecf20Sopenharmony_ci CIO2_CDMAC0_DMA_INTR_ON_FE | 4798c2ecf20Sopenharmony_ci CIO2_CDMAC0_FBPT_UPDATE_FIFO_FULL | 4808c2ecf20Sopenharmony_ci CIO2_CDMAC0_DMA_EN | 4818c2ecf20Sopenharmony_ci CIO2_CDMAC0_DMA_INTR_ON_FS | 4828c2ecf20Sopenharmony_ci CIO2_CDMAC0_DMA_HALTED, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN)); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci writel(1 << CIO2_CDMAC1_LINENUMUPDATE_SHIFT, 4858c2ecf20Sopenharmony_ci base + CIO2_REG_CDMAC1(CIO2_DMA_CHAN)); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci writel(0, base + CIO2_REG_PBM_FOPN_ABORT); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci writel(CIO2_PXM_FRF_CFG_CRC_TH << CIO2_PXM_FRF_CFG_CRC_TH_SHIFT | 4908c2ecf20Sopenharmony_ci CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NR | 4918c2ecf20Sopenharmony_ci CIO2_PXM_FRF_CFG_MSK_ECC_RE | 4928c2ecf20Sopenharmony_ci CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NE, 4938c2ecf20Sopenharmony_ci base + CIO2_REG_PXM_FRF_CFG(q->csi2.port)); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* Clear interrupts */ 4968c2ecf20Sopenharmony_ci writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_CLEAR); 4978c2ecf20Sopenharmony_ci writel(~0, base + CIO2_REG_INT_STS_EXT_OE); 4988c2ecf20Sopenharmony_ci writel(~0, base + CIO2_REG_INT_STS_EXT_IE); 4998c2ecf20Sopenharmony_ci writel(~0, base + CIO2_REG_INT_STS); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* Enable devices, starting from the last device in the pipe */ 5028c2ecf20Sopenharmony_ci writel(1, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE); 5038c2ecf20Sopenharmony_ci writel(1, q->csi_rx_base + CIO2_REG_CSIRX_ENABLE); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic void cio2_hw_exit(struct cio2_device *cio2, struct cio2_queue *q) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci void __iomem *const base = cio2->base; 5118c2ecf20Sopenharmony_ci unsigned int i; 5128c2ecf20Sopenharmony_ci u32 value; 5138c2ecf20Sopenharmony_ci int ret; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Disable CSI receiver and MIPI backend devices */ 5168c2ecf20Sopenharmony_ci writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK); 5178c2ecf20Sopenharmony_ci writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_ENABLE); 5188c2ecf20Sopenharmony_ci writel(0, q->csi_rx_base + CIO2_REG_CSIRX_ENABLE); 5198c2ecf20Sopenharmony_ci writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* Halt DMA */ 5228c2ecf20Sopenharmony_ci writel(0, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN)); 5238c2ecf20Sopenharmony_ci ret = readl_poll_timeout(base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN), 5248c2ecf20Sopenharmony_ci value, value & CIO2_CDMAC0_DMA_HALTED, 5258c2ecf20Sopenharmony_ci 4000, 2000000); 5268c2ecf20Sopenharmony_ci if (ret) 5278c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, 5288c2ecf20Sopenharmony_ci "DMA %i can not be halted\n", CIO2_DMA_CHAN); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci for (i = 0; i < CIO2_NUM_PORTS; i++) { 5318c2ecf20Sopenharmony_ci writel(readl(base + CIO2_REG_PXM_FRF_CFG(i)) | 5328c2ecf20Sopenharmony_ci CIO2_PXM_FRF_CFG_ABORT, base + CIO2_REG_PXM_FRF_CFG(i)); 5338c2ecf20Sopenharmony_ci writel(readl(base + CIO2_REG_PBM_FOPN_ABORT) | 5348c2ecf20Sopenharmony_ci CIO2_PBM_FOPN_ABORT(i), base + CIO2_REG_PBM_FOPN_ABORT); 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct device *dev = &cio2->pci_dev->dev; 5418c2ecf20Sopenharmony_ci struct cio2_queue *q = cio2->cur_queue; 5428c2ecf20Sopenharmony_ci struct cio2_fbpt_entry *entry; 5438c2ecf20Sopenharmony_ci u64 ns = ktime_get_ns(); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (dma_chan >= CIO2_QUEUES) { 5468c2ecf20Sopenharmony_ci dev_err(dev, "bad DMA channel %i\n", dma_chan); 5478c2ecf20Sopenharmony_ci return; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci entry = &q->fbpt[q->bufs_first * CIO2_MAX_LOPS]; 5518c2ecf20Sopenharmony_ci if (entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID) { 5528c2ecf20Sopenharmony_ci dev_warn(&cio2->pci_dev->dev, 5538c2ecf20Sopenharmony_ci "no ready buffers found on DMA channel %u\n", 5548c2ecf20Sopenharmony_ci dma_chan); 5558c2ecf20Sopenharmony_ci return; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* Find out which buffer(s) are ready */ 5598c2ecf20Sopenharmony_ci do { 5608c2ecf20Sopenharmony_ci struct cio2_buffer *b; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci b = q->bufs[q->bufs_first]; 5638c2ecf20Sopenharmony_ci if (b) { 5648c2ecf20Sopenharmony_ci unsigned int bytes = entry[1].second_entry.num_of_bytes; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci q->bufs[q->bufs_first] = NULL; 5678c2ecf20Sopenharmony_ci atomic_dec(&q->bufs_queued); 5688c2ecf20Sopenharmony_ci dev_dbg(&cio2->pci_dev->dev, 5698c2ecf20Sopenharmony_ci "buffer %i done\n", b->vbb.vb2_buf.index); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci b->vbb.vb2_buf.timestamp = ns; 5728c2ecf20Sopenharmony_ci b->vbb.field = V4L2_FIELD_NONE; 5738c2ecf20Sopenharmony_ci b->vbb.sequence = atomic_read(&q->frame_sequence); 5748c2ecf20Sopenharmony_ci if (b->vbb.vb2_buf.planes[0].length != bytes) 5758c2ecf20Sopenharmony_ci dev_warn(dev, "buffer length is %d received %d\n", 5768c2ecf20Sopenharmony_ci b->vbb.vb2_buf.planes[0].length, 5778c2ecf20Sopenharmony_ci bytes); 5788c2ecf20Sopenharmony_ci vb2_buffer_done(&b->vbb.vb2_buf, VB2_BUF_STATE_DONE); 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci atomic_inc(&q->frame_sequence); 5818c2ecf20Sopenharmony_ci cio2_fbpt_entry_init_dummy(cio2, entry); 5828c2ecf20Sopenharmony_ci q->bufs_first = (q->bufs_first + 1) % CIO2_MAX_BUFFERS; 5838c2ecf20Sopenharmony_ci entry = &q->fbpt[q->bufs_first * CIO2_MAX_LOPS]; 5848c2ecf20Sopenharmony_ci } while (!(entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID)); 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic void cio2_queue_event_sof(struct cio2_device *cio2, struct cio2_queue *q) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci /* 5908c2ecf20Sopenharmony_ci * For the user space camera control algorithms it is essential 5918c2ecf20Sopenharmony_ci * to know when the reception of a frame has begun. That's often 5928c2ecf20Sopenharmony_ci * the best timing information to get from the hardware. 5938c2ecf20Sopenharmony_ci */ 5948c2ecf20Sopenharmony_ci struct v4l2_event event = { 5958c2ecf20Sopenharmony_ci .type = V4L2_EVENT_FRAME_SYNC, 5968c2ecf20Sopenharmony_ci .u.frame_sync.frame_sequence = atomic_read(&q->frame_sequence), 5978c2ecf20Sopenharmony_ci }; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci v4l2_event_queue(q->subdev.devnode, &event); 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic const char *const cio2_irq_errs[] = { 6038c2ecf20Sopenharmony_ci "single packet header error corrected", 6048c2ecf20Sopenharmony_ci "multiple packet header errors detected", 6058c2ecf20Sopenharmony_ci "payload checksum (CRC) error", 6068c2ecf20Sopenharmony_ci "fifo overflow", 6078c2ecf20Sopenharmony_ci "reserved short packet data type detected", 6088c2ecf20Sopenharmony_ci "reserved long packet data type detected", 6098c2ecf20Sopenharmony_ci "incomplete long packet detected", 6108c2ecf20Sopenharmony_ci "frame sync error", 6118c2ecf20Sopenharmony_ci "line sync error", 6128c2ecf20Sopenharmony_ci "DPHY start of transmission error", 6138c2ecf20Sopenharmony_ci "DPHY synchronization error", 6148c2ecf20Sopenharmony_ci "escape mode error", 6158c2ecf20Sopenharmony_ci "escape mode trigger event", 6168c2ecf20Sopenharmony_ci "escape mode ultra-low power state for data lane(s)", 6178c2ecf20Sopenharmony_ci "escape mode ultra-low power state exit for clock lane", 6188c2ecf20Sopenharmony_ci "inter-frame short packet discarded", 6198c2ecf20Sopenharmony_ci "inter-frame long packet discarded", 6208c2ecf20Sopenharmony_ci "non-matching Long Packet stalled", 6218c2ecf20Sopenharmony_ci}; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic const char *const cio2_port_errs[] = { 6248c2ecf20Sopenharmony_ci "ECC recoverable", 6258c2ecf20Sopenharmony_ci "DPHY not recoverable", 6268c2ecf20Sopenharmony_ci "ECC not recoverable", 6278c2ecf20Sopenharmony_ci "CRC error", 6288c2ecf20Sopenharmony_ci "INTERFRAMEDATA", 6298c2ecf20Sopenharmony_ci "PKT2SHORT", 6308c2ecf20Sopenharmony_ci "PKT2LONG", 6318c2ecf20Sopenharmony_ci}; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic void cio2_irq_handle_once(struct cio2_device *cio2, u32 int_status) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci void __iomem *const base = cio2->base; 6368c2ecf20Sopenharmony_ci struct device *dev = &cio2->pci_dev->dev; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (int_status & CIO2_INT_IOOE) { 6398c2ecf20Sopenharmony_ci /* 6408c2ecf20Sopenharmony_ci * Interrupt on Output Error: 6418c2ecf20Sopenharmony_ci * 1) SRAM is full and FS received, or 6428c2ecf20Sopenharmony_ci * 2) An invalid bit detected by DMA. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_ci u32 oe_status, oe_clear; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci oe_clear = readl(base + CIO2_REG_INT_STS_EXT_OE); 6478c2ecf20Sopenharmony_ci oe_status = oe_clear; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (oe_status & CIO2_INT_EXT_OE_DMAOE_MASK) { 6508c2ecf20Sopenharmony_ci dev_err(dev, "DMA output error: 0x%x\n", 6518c2ecf20Sopenharmony_ci (oe_status & CIO2_INT_EXT_OE_DMAOE_MASK) 6528c2ecf20Sopenharmony_ci >> CIO2_INT_EXT_OE_DMAOE_SHIFT); 6538c2ecf20Sopenharmony_ci oe_status &= ~CIO2_INT_EXT_OE_DMAOE_MASK; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci if (oe_status & CIO2_INT_EXT_OE_OES_MASK) { 6568c2ecf20Sopenharmony_ci dev_err(dev, "DMA output error on CSI2 buses: 0x%x\n", 6578c2ecf20Sopenharmony_ci (oe_status & CIO2_INT_EXT_OE_OES_MASK) 6588c2ecf20Sopenharmony_ci >> CIO2_INT_EXT_OE_OES_SHIFT); 6598c2ecf20Sopenharmony_ci oe_status &= ~CIO2_INT_EXT_OE_OES_MASK; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci writel(oe_clear, base + CIO2_REG_INT_STS_EXT_OE); 6628c2ecf20Sopenharmony_ci if (oe_status) 6638c2ecf20Sopenharmony_ci dev_warn(dev, "unknown interrupt 0x%x on OE\n", 6648c2ecf20Sopenharmony_ci oe_status); 6658c2ecf20Sopenharmony_ci int_status &= ~CIO2_INT_IOOE; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (int_status & CIO2_INT_IOC_MASK) { 6698c2ecf20Sopenharmony_ci /* DMA IO done -- frame ready */ 6708c2ecf20Sopenharmony_ci u32 clr = 0; 6718c2ecf20Sopenharmony_ci unsigned int d; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci for (d = 0; d < CIO2_NUM_DMA_CHAN; d++) 6748c2ecf20Sopenharmony_ci if (int_status & CIO2_INT_IOC(d)) { 6758c2ecf20Sopenharmony_ci clr |= CIO2_INT_IOC(d); 6768c2ecf20Sopenharmony_ci cio2_buffer_done(cio2, d); 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci int_status &= ~clr; 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (int_status & CIO2_INT_IOS_IOLN_MASK) { 6828c2ecf20Sopenharmony_ci /* DMA IO starts or reached specified line */ 6838c2ecf20Sopenharmony_ci u32 clr = 0; 6848c2ecf20Sopenharmony_ci unsigned int d; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci for (d = 0; d < CIO2_NUM_DMA_CHAN; d++) 6878c2ecf20Sopenharmony_ci if (int_status & CIO2_INT_IOS_IOLN(d)) { 6888c2ecf20Sopenharmony_ci clr |= CIO2_INT_IOS_IOLN(d); 6898c2ecf20Sopenharmony_ci if (d == CIO2_DMA_CHAN) 6908c2ecf20Sopenharmony_ci cio2_queue_event_sof(cio2, 6918c2ecf20Sopenharmony_ci cio2->cur_queue); 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci int_status &= ~clr; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (int_status & (CIO2_INT_IOIE | CIO2_INT_IOIRQ)) { 6978c2ecf20Sopenharmony_ci /* CSI2 receiver (error) interrupt */ 6988c2ecf20Sopenharmony_ci u32 ie_status, ie_clear; 6998c2ecf20Sopenharmony_ci unsigned int port; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci ie_clear = readl(base + CIO2_REG_INT_STS_EXT_IE); 7028c2ecf20Sopenharmony_ci ie_status = ie_clear; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci for (port = 0; port < CIO2_NUM_PORTS; port++) { 7058c2ecf20Sopenharmony_ci u32 port_status = (ie_status >> (port * 8)) & 0xff; 7068c2ecf20Sopenharmony_ci u32 err_mask = BIT_MASK(ARRAY_SIZE(cio2_port_errs)) - 1; 7078c2ecf20Sopenharmony_ci void __iomem *const csi_rx_base = 7088c2ecf20Sopenharmony_ci base + CIO2_REG_PIPE_BASE(port); 7098c2ecf20Sopenharmony_ci unsigned int i; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci while (port_status & err_mask) { 7128c2ecf20Sopenharmony_ci i = ffs(port_status) - 1; 7138c2ecf20Sopenharmony_ci dev_err(dev, "port %i error %s\n", 7148c2ecf20Sopenharmony_ci port, cio2_port_errs[i]); 7158c2ecf20Sopenharmony_ci ie_status &= ~BIT(port * 8 + i); 7168c2ecf20Sopenharmony_ci port_status &= ~BIT(i); 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci if (ie_status & CIO2_INT_EXT_IE_IRQ(port)) { 7208c2ecf20Sopenharmony_ci u32 csi2_status, csi2_clear; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci csi2_status = readl(csi_rx_base + 7238c2ecf20Sopenharmony_ci CIO2_REG_IRQCTRL_STATUS); 7248c2ecf20Sopenharmony_ci csi2_clear = csi2_status; 7258c2ecf20Sopenharmony_ci err_mask = 7268c2ecf20Sopenharmony_ci BIT_MASK(ARRAY_SIZE(cio2_irq_errs)) - 1; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci while (csi2_status & err_mask) { 7298c2ecf20Sopenharmony_ci i = ffs(csi2_status) - 1; 7308c2ecf20Sopenharmony_ci dev_err(dev, 7318c2ecf20Sopenharmony_ci "CSI-2 receiver port %i: %s\n", 7328c2ecf20Sopenharmony_ci port, cio2_irq_errs[i]); 7338c2ecf20Sopenharmony_ci csi2_status &= ~BIT(i); 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci writel(csi2_clear, 7378c2ecf20Sopenharmony_ci csi_rx_base + CIO2_REG_IRQCTRL_CLEAR); 7388c2ecf20Sopenharmony_ci if (csi2_status) 7398c2ecf20Sopenharmony_ci dev_warn(dev, 7408c2ecf20Sopenharmony_ci "unknown CSI2 error 0x%x on port %i\n", 7418c2ecf20Sopenharmony_ci csi2_status, port); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci ie_status &= ~CIO2_INT_EXT_IE_IRQ(port); 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci writel(ie_clear, base + CIO2_REG_INT_STS_EXT_IE); 7488c2ecf20Sopenharmony_ci if (ie_status) 7498c2ecf20Sopenharmony_ci dev_warn(dev, "unknown interrupt 0x%x on IE\n", 7508c2ecf20Sopenharmony_ci ie_status); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci int_status &= ~(CIO2_INT_IOIE | CIO2_INT_IOIRQ); 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci if (int_status) 7568c2ecf20Sopenharmony_ci dev_warn(dev, "unknown interrupt 0x%x on INT\n", int_status); 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic irqreturn_t cio2_irq(int irq, void *cio2_ptr) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci struct cio2_device *cio2 = cio2_ptr; 7628c2ecf20Sopenharmony_ci void __iomem *const base = cio2->base; 7638c2ecf20Sopenharmony_ci struct device *dev = &cio2->pci_dev->dev; 7648c2ecf20Sopenharmony_ci u32 int_status; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci int_status = readl(base + CIO2_REG_INT_STS); 7678c2ecf20Sopenharmony_ci dev_dbg(dev, "isr enter - interrupt status 0x%x\n", int_status); 7688c2ecf20Sopenharmony_ci if (!int_status) 7698c2ecf20Sopenharmony_ci return IRQ_NONE; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci do { 7728c2ecf20Sopenharmony_ci writel(int_status, base + CIO2_REG_INT_STS); 7738c2ecf20Sopenharmony_ci cio2_irq_handle_once(cio2, int_status); 7748c2ecf20Sopenharmony_ci int_status = readl(base + CIO2_REG_INT_STS); 7758c2ecf20Sopenharmony_ci if (int_status) 7768c2ecf20Sopenharmony_ci dev_dbg(dev, "pending status 0x%x\n", int_status); 7778c2ecf20Sopenharmony_ci } while (int_status); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci/**************** Videobuf2 interface ****************/ 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic void cio2_vb2_return_all_buffers(struct cio2_queue *q, 7858c2ecf20Sopenharmony_ci enum vb2_buffer_state state) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci unsigned int i; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci for (i = 0; i < CIO2_MAX_BUFFERS; i++) { 7908c2ecf20Sopenharmony_ci if (q->bufs[i]) { 7918c2ecf20Sopenharmony_ci atomic_dec(&q->bufs_queued); 7928c2ecf20Sopenharmony_ci vb2_buffer_done(&q->bufs[i]->vbb.vb2_buf, 7938c2ecf20Sopenharmony_ci state); 7948c2ecf20Sopenharmony_ci q->bufs[i] = NULL; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic int cio2_vb2_queue_setup(struct vb2_queue *vq, 8008c2ecf20Sopenharmony_ci unsigned int *num_buffers, 8018c2ecf20Sopenharmony_ci unsigned int *num_planes, 8028c2ecf20Sopenharmony_ci unsigned int sizes[], 8038c2ecf20Sopenharmony_ci struct device *alloc_devs[]) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci struct cio2_device *cio2 = vb2_get_drv_priv(vq); 8068c2ecf20Sopenharmony_ci struct cio2_queue *q = vb2q_to_cio2_queue(vq); 8078c2ecf20Sopenharmony_ci unsigned int i; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci *num_planes = q->format.num_planes; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci for (i = 0; i < *num_planes; ++i) { 8128c2ecf20Sopenharmony_ci sizes[i] = q->format.plane_fmt[i].sizeimage; 8138c2ecf20Sopenharmony_ci alloc_devs[i] = &cio2->pci_dev->dev; 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci *num_buffers = clamp_val(*num_buffers, 1, CIO2_MAX_BUFFERS); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* Initialize buffer queue */ 8198c2ecf20Sopenharmony_ci for (i = 0; i < CIO2_MAX_BUFFERS; i++) { 8208c2ecf20Sopenharmony_ci q->bufs[i] = NULL; 8218c2ecf20Sopenharmony_ci cio2_fbpt_entry_init_dummy(cio2, &q->fbpt[i * CIO2_MAX_LOPS]); 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci atomic_set(&q->bufs_queued, 0); 8248c2ecf20Sopenharmony_ci q->bufs_first = 0; 8258c2ecf20Sopenharmony_ci q->bufs_next = 0; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci/* Called after each buffer is allocated */ 8318c2ecf20Sopenharmony_cistatic int cio2_vb2_buf_init(struct vb2_buffer *vb) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue); 8348c2ecf20Sopenharmony_ci struct device *dev = &cio2->pci_dev->dev; 8358c2ecf20Sopenharmony_ci struct cio2_buffer *b = 8368c2ecf20Sopenharmony_ci container_of(vb, struct cio2_buffer, vbb.vb2_buf); 8378c2ecf20Sopenharmony_ci unsigned int pages = PFN_UP(vb->planes[0].length); 8388c2ecf20Sopenharmony_ci unsigned int lops = DIV_ROUND_UP(pages + 1, CIO2_LOP_ENTRIES); 8398c2ecf20Sopenharmony_ci struct sg_table *sg; 8408c2ecf20Sopenharmony_ci struct sg_dma_page_iter sg_iter; 8418c2ecf20Sopenharmony_ci unsigned int i, j; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (lops <= 0 || lops > CIO2_MAX_LOPS) { 8448c2ecf20Sopenharmony_ci dev_err(dev, "%s: bad buffer size (%i)\n", __func__, 8458c2ecf20Sopenharmony_ci vb->planes[0].length); 8468c2ecf20Sopenharmony_ci return -ENOSPC; /* Should never happen */ 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci memset(b->lop, 0, sizeof(b->lop)); 8508c2ecf20Sopenharmony_ci /* Allocate LOP table */ 8518c2ecf20Sopenharmony_ci for (i = 0; i < lops; i++) { 8528c2ecf20Sopenharmony_ci b->lop[i] = dma_alloc_coherent(dev, PAGE_SIZE, 8538c2ecf20Sopenharmony_ci &b->lop_bus_addr[i], GFP_KERNEL); 8548c2ecf20Sopenharmony_ci if (!b->lop[i]) 8558c2ecf20Sopenharmony_ci goto fail; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci /* Fill LOP */ 8598c2ecf20Sopenharmony_ci sg = vb2_dma_sg_plane_desc(vb, 0); 8608c2ecf20Sopenharmony_ci if (!sg) 8618c2ecf20Sopenharmony_ci return -ENOMEM; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (sg->nents && sg->sgl) 8648c2ecf20Sopenharmony_ci b->offset = sg->sgl->offset; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci i = j = 0; 8678c2ecf20Sopenharmony_ci for_each_sg_dma_page(sg->sgl, &sg_iter, sg->nents, 0) { 8688c2ecf20Sopenharmony_ci if (!pages--) 8698c2ecf20Sopenharmony_ci break; 8708c2ecf20Sopenharmony_ci b->lop[i][j] = PFN_DOWN(sg_page_iter_dma_address(&sg_iter)); 8718c2ecf20Sopenharmony_ci j++; 8728c2ecf20Sopenharmony_ci if (j == CIO2_LOP_ENTRIES) { 8738c2ecf20Sopenharmony_ci i++; 8748c2ecf20Sopenharmony_ci j = 0; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci b->lop[i][j] = PFN_DOWN(cio2->dummy_page_bus_addr); 8798c2ecf20Sopenharmony_ci return 0; 8808c2ecf20Sopenharmony_cifail: 8818c2ecf20Sopenharmony_ci while (i--) 8828c2ecf20Sopenharmony_ci dma_free_coherent(dev, PAGE_SIZE, b->lop[i], b->lop_bus_addr[i]); 8838c2ecf20Sopenharmony_ci return -ENOMEM; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci/* Transfer buffer ownership to cio2 */ 8878c2ecf20Sopenharmony_cistatic void cio2_vb2_buf_queue(struct vb2_buffer *vb) 8888c2ecf20Sopenharmony_ci{ 8898c2ecf20Sopenharmony_ci struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue); 8908c2ecf20Sopenharmony_ci struct cio2_queue *q = 8918c2ecf20Sopenharmony_ci container_of(vb->vb2_queue, struct cio2_queue, vbq); 8928c2ecf20Sopenharmony_ci struct cio2_buffer *b = 8938c2ecf20Sopenharmony_ci container_of(vb, struct cio2_buffer, vbb.vb2_buf); 8948c2ecf20Sopenharmony_ci struct cio2_fbpt_entry *entry; 8958c2ecf20Sopenharmony_ci unsigned long flags; 8968c2ecf20Sopenharmony_ci unsigned int i, j, next = q->bufs_next; 8978c2ecf20Sopenharmony_ci int bufs_queued = atomic_inc_return(&q->bufs_queued); 8988c2ecf20Sopenharmony_ci u32 fbpt_rp; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci dev_dbg(&cio2->pci_dev->dev, "queue buffer %d\n", vb->index); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci /* 9038c2ecf20Sopenharmony_ci * This code queues the buffer to the CIO2 DMA engine, which starts 9048c2ecf20Sopenharmony_ci * running once streaming has started. It is possible that this code 9058c2ecf20Sopenharmony_ci * gets pre-empted due to increased CPU load. Upon this, the driver 9068c2ecf20Sopenharmony_ci * does not get an opportunity to queue new buffers to the CIO2 DMA 9078c2ecf20Sopenharmony_ci * engine. When the DMA engine encounters an FBPT entry without the 9088c2ecf20Sopenharmony_ci * VALID bit set, the DMA engine halts, which requires a restart of 9098c2ecf20Sopenharmony_ci * the DMA engine and sensor, to continue streaming. 9108c2ecf20Sopenharmony_ci * This is not desired and is highly unlikely given that there are 9118c2ecf20Sopenharmony_ci * 32 FBPT entries that the DMA engine needs to process, to run into 9128c2ecf20Sopenharmony_ci * an FBPT entry, without the VALID bit set. We try to mitigate this 9138c2ecf20Sopenharmony_ci * by disabling interrupts for the duration of this queueing. 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_ci local_irq_save(flags); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci fbpt_rp = (readl(cio2->base + CIO2_REG_CDMARI(CIO2_DMA_CHAN)) 9188c2ecf20Sopenharmony_ci >> CIO2_CDMARI_FBPT_RP_SHIFT) 9198c2ecf20Sopenharmony_ci & CIO2_CDMARI_FBPT_RP_MASK; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci /* 9228c2ecf20Sopenharmony_ci * fbpt_rp is the fbpt entry that the dma is currently working 9238c2ecf20Sopenharmony_ci * on, but since it could jump to next entry at any time, 9248c2ecf20Sopenharmony_ci * assume that we might already be there. 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_ci fbpt_rp = (fbpt_rp + 1) % CIO2_MAX_BUFFERS; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (bufs_queued <= 1 || fbpt_rp == next) 9298c2ecf20Sopenharmony_ci /* Buffers were drained */ 9308c2ecf20Sopenharmony_ci next = (fbpt_rp + 1) % CIO2_MAX_BUFFERS; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci for (i = 0; i < CIO2_MAX_BUFFERS; i++) { 9338c2ecf20Sopenharmony_ci /* 9348c2ecf20Sopenharmony_ci * We have allocated CIO2_MAX_BUFFERS circularly for the 9358c2ecf20Sopenharmony_ci * hw, the user has requested N buffer queue. The driver 9368c2ecf20Sopenharmony_ci * ensures N <= CIO2_MAX_BUFFERS and guarantees that whenever 9378c2ecf20Sopenharmony_ci * user queues a buffer, there necessarily is a free buffer. 9388c2ecf20Sopenharmony_ci */ 9398c2ecf20Sopenharmony_ci if (!q->bufs[next]) { 9408c2ecf20Sopenharmony_ci q->bufs[next] = b; 9418c2ecf20Sopenharmony_ci entry = &q->fbpt[next * CIO2_MAX_LOPS]; 9428c2ecf20Sopenharmony_ci cio2_fbpt_entry_init_buf(cio2, b, entry); 9438c2ecf20Sopenharmony_ci local_irq_restore(flags); 9448c2ecf20Sopenharmony_ci q->bufs_next = (next + 1) % CIO2_MAX_BUFFERS; 9458c2ecf20Sopenharmony_ci for (j = 0; j < vb->num_planes; j++) 9468c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, j, 9478c2ecf20Sopenharmony_ci q->format.plane_fmt[j].sizeimage); 9488c2ecf20Sopenharmony_ci return; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci dev_dbg(&cio2->pci_dev->dev, "entry %i was full!\n", next); 9528c2ecf20Sopenharmony_ci next = (next + 1) % CIO2_MAX_BUFFERS; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci local_irq_restore(flags); 9568c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, "error: all cio2 entries were full!\n"); 9578c2ecf20Sopenharmony_ci atomic_dec(&q->bufs_queued); 9588c2ecf20Sopenharmony_ci vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci/* Called when each buffer is freed */ 9628c2ecf20Sopenharmony_cistatic void cio2_vb2_buf_cleanup(struct vb2_buffer *vb) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue); 9658c2ecf20Sopenharmony_ci struct cio2_buffer *b = 9668c2ecf20Sopenharmony_ci container_of(vb, struct cio2_buffer, vbb.vb2_buf); 9678c2ecf20Sopenharmony_ci unsigned int i; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci /* Free LOP table */ 9708c2ecf20Sopenharmony_ci for (i = 0; i < CIO2_MAX_LOPS; i++) { 9718c2ecf20Sopenharmony_ci if (b->lop[i]) 9728c2ecf20Sopenharmony_ci dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE, 9738c2ecf20Sopenharmony_ci b->lop[i], b->lop_bus_addr[i]); 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_cistatic int cio2_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci struct cio2_queue *q = vb2q_to_cio2_queue(vq); 9808c2ecf20Sopenharmony_ci struct cio2_device *cio2 = vb2_get_drv_priv(vq); 9818c2ecf20Sopenharmony_ci int r; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci cio2->cur_queue = q; 9848c2ecf20Sopenharmony_ci atomic_set(&q->frame_sequence, 0); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci r = pm_runtime_get_sync(&cio2->pci_dev->dev); 9878c2ecf20Sopenharmony_ci if (r < 0) { 9888c2ecf20Sopenharmony_ci dev_info(&cio2->pci_dev->dev, "failed to set power %d\n", r); 9898c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&cio2->pci_dev->dev); 9908c2ecf20Sopenharmony_ci return r; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci r = media_pipeline_start(&q->vdev.entity, &q->pipe); 9948c2ecf20Sopenharmony_ci if (r) 9958c2ecf20Sopenharmony_ci goto fail_pipeline; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci r = cio2_hw_init(cio2, q); 9988c2ecf20Sopenharmony_ci if (r) 9998c2ecf20Sopenharmony_ci goto fail_hw; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* Start streaming on sensor */ 10028c2ecf20Sopenharmony_ci r = v4l2_subdev_call(q->sensor, video, s_stream, 1); 10038c2ecf20Sopenharmony_ci if (r) 10048c2ecf20Sopenharmony_ci goto fail_csi2_subdev; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci cio2->streaming = true; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci return 0; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_cifail_csi2_subdev: 10118c2ecf20Sopenharmony_ci cio2_hw_exit(cio2, q); 10128c2ecf20Sopenharmony_cifail_hw: 10138c2ecf20Sopenharmony_ci media_pipeline_stop(&q->vdev.entity); 10148c2ecf20Sopenharmony_cifail_pipeline: 10158c2ecf20Sopenharmony_ci dev_dbg(&cio2->pci_dev->dev, "failed to start streaming (%d)\n", r); 10168c2ecf20Sopenharmony_ci cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_QUEUED); 10178c2ecf20Sopenharmony_ci pm_runtime_put(&cio2->pci_dev->dev); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci return r; 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_cistatic void cio2_vb2_stop_streaming(struct vb2_queue *vq) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci struct cio2_queue *q = vb2q_to_cio2_queue(vq); 10258c2ecf20Sopenharmony_ci struct cio2_device *cio2 = vb2_get_drv_priv(vq); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (v4l2_subdev_call(q->sensor, video, s_stream, 0)) 10288c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, 10298c2ecf20Sopenharmony_ci "failed to stop sensor streaming\n"); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci cio2_hw_exit(cio2, q); 10328c2ecf20Sopenharmony_ci synchronize_irq(cio2->pci_dev->irq); 10338c2ecf20Sopenharmony_ci cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_ERROR); 10348c2ecf20Sopenharmony_ci media_pipeline_stop(&q->vdev.entity); 10358c2ecf20Sopenharmony_ci pm_runtime_put(&cio2->pci_dev->dev); 10368c2ecf20Sopenharmony_ci cio2->streaming = false; 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic const struct vb2_ops cio2_vb2_ops = { 10408c2ecf20Sopenharmony_ci .buf_init = cio2_vb2_buf_init, 10418c2ecf20Sopenharmony_ci .buf_queue = cio2_vb2_buf_queue, 10428c2ecf20Sopenharmony_ci .buf_cleanup = cio2_vb2_buf_cleanup, 10438c2ecf20Sopenharmony_ci .queue_setup = cio2_vb2_queue_setup, 10448c2ecf20Sopenharmony_ci .start_streaming = cio2_vb2_start_streaming, 10458c2ecf20Sopenharmony_ci .stop_streaming = cio2_vb2_stop_streaming, 10468c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 10478c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 10488c2ecf20Sopenharmony_ci}; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci/**************** V4L2 interface ****************/ 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic int cio2_v4l2_querycap(struct file *file, void *fh, 10538c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci struct cio2_device *cio2 = video_drvdata(file); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci strscpy(cap->driver, CIO2_NAME, sizeof(cap->driver)); 10588c2ecf20Sopenharmony_ci strscpy(cap->card, CIO2_DEVICE_NAME, sizeof(cap->card)); 10598c2ecf20Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), 10608c2ecf20Sopenharmony_ci "PCI:%s", pci_name(cio2->pci_dev)); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci return 0; 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic int cio2_v4l2_enum_fmt(struct file *file, void *fh, 10668c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 10678c2ecf20Sopenharmony_ci{ 10688c2ecf20Sopenharmony_ci if (f->index >= ARRAY_SIZE(formats)) 10698c2ecf20Sopenharmony_ci return -EINVAL; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci f->pixelformat = formats[f->index].fourcc; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci return 0; 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci/* The format is validated in cio2_video_link_validate() */ 10778c2ecf20Sopenharmony_cistatic int cio2_v4l2_g_fmt(struct file *file, void *fh, struct v4l2_format *f) 10788c2ecf20Sopenharmony_ci{ 10798c2ecf20Sopenharmony_ci struct cio2_queue *q = file_to_cio2_queue(file); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci f->fmt.pix_mp = q->format; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci return 0; 10848c2ecf20Sopenharmony_ci} 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_cistatic int cio2_v4l2_try_fmt(struct file *file, void *fh, struct v4l2_format *f) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci const struct ipu3_cio2_fmt *fmt; 10898c2ecf20Sopenharmony_ci struct v4l2_pix_format_mplane *mpix = &f->fmt.pix_mp; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci fmt = cio2_find_format(&mpix->pixelformat, NULL); 10928c2ecf20Sopenharmony_ci if (!fmt) 10938c2ecf20Sopenharmony_ci fmt = &formats[0]; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* Only supports up to 4224x3136 */ 10968c2ecf20Sopenharmony_ci if (mpix->width > CIO2_IMAGE_MAX_WIDTH) 10978c2ecf20Sopenharmony_ci mpix->width = CIO2_IMAGE_MAX_WIDTH; 10988c2ecf20Sopenharmony_ci if (mpix->height > CIO2_IMAGE_MAX_LENGTH) 10998c2ecf20Sopenharmony_ci mpix->height = CIO2_IMAGE_MAX_LENGTH; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci mpix->num_planes = 1; 11028c2ecf20Sopenharmony_ci mpix->pixelformat = fmt->fourcc; 11038c2ecf20Sopenharmony_ci mpix->colorspace = V4L2_COLORSPACE_RAW; 11048c2ecf20Sopenharmony_ci mpix->field = V4L2_FIELD_NONE; 11058c2ecf20Sopenharmony_ci memset(mpix->reserved, 0, sizeof(mpix->reserved)); 11068c2ecf20Sopenharmony_ci mpix->plane_fmt[0].bytesperline = cio2_bytesperline(mpix->width); 11078c2ecf20Sopenharmony_ci mpix->plane_fmt[0].sizeimage = mpix->plane_fmt[0].bytesperline * 11088c2ecf20Sopenharmony_ci mpix->height; 11098c2ecf20Sopenharmony_ci memset(mpix->plane_fmt[0].reserved, 0, 11108c2ecf20Sopenharmony_ci sizeof(mpix->plane_fmt[0].reserved)); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci /* use default */ 11138c2ecf20Sopenharmony_ci mpix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 11148c2ecf20Sopenharmony_ci mpix->quantization = V4L2_QUANTIZATION_DEFAULT; 11158c2ecf20Sopenharmony_ci mpix->xfer_func = V4L2_XFER_FUNC_DEFAULT; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci return 0; 11188c2ecf20Sopenharmony_ci} 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_cistatic int cio2_v4l2_s_fmt(struct file *file, void *fh, struct v4l2_format *f) 11218c2ecf20Sopenharmony_ci{ 11228c2ecf20Sopenharmony_ci struct cio2_queue *q = file_to_cio2_queue(file); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci cio2_v4l2_try_fmt(file, fh, f); 11258c2ecf20Sopenharmony_ci q->format = f->fmt.pix_mp; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci return 0; 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic int 11318c2ecf20Sopenharmony_cicio2_video_enum_input(struct file *file, void *fh, struct v4l2_input *input) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci if (input->index > 0) 11348c2ecf20Sopenharmony_ci return -EINVAL; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci strscpy(input->name, "camera", sizeof(input->name)); 11378c2ecf20Sopenharmony_ci input->type = V4L2_INPUT_TYPE_CAMERA; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci return 0; 11408c2ecf20Sopenharmony_ci} 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_cistatic int 11438c2ecf20Sopenharmony_cicio2_video_g_input(struct file *file, void *fh, unsigned int *input) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci *input = 0; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci return 0; 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_cistatic int 11518c2ecf20Sopenharmony_cicio2_video_s_input(struct file *file, void *fh, unsigned int input) 11528c2ecf20Sopenharmony_ci{ 11538c2ecf20Sopenharmony_ci return input == 0 ? 0 : -EINVAL; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations cio2_v4l2_fops = { 11578c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 11588c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 11598c2ecf20Sopenharmony_ci .open = v4l2_fh_open, 11608c2ecf20Sopenharmony_ci .release = vb2_fop_release, 11618c2ecf20Sopenharmony_ci .poll = vb2_fop_poll, 11628c2ecf20Sopenharmony_ci .mmap = vb2_fop_mmap, 11638c2ecf20Sopenharmony_ci}; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops cio2_v4l2_ioctl_ops = { 11668c2ecf20Sopenharmony_ci .vidioc_querycap = cio2_v4l2_querycap, 11678c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = cio2_v4l2_enum_fmt, 11688c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap_mplane = cio2_v4l2_g_fmt, 11698c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap_mplane = cio2_v4l2_s_fmt, 11708c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap_mplane = cio2_v4l2_try_fmt, 11718c2ecf20Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 11728c2ecf20Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 11738c2ecf20Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 11748c2ecf20Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 11758c2ecf20Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 11768c2ecf20Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 11778c2ecf20Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 11788c2ecf20Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 11798c2ecf20Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 11808c2ecf20Sopenharmony_ci .vidioc_enum_input = cio2_video_enum_input, 11818c2ecf20Sopenharmony_ci .vidioc_g_input = cio2_video_g_input, 11828c2ecf20Sopenharmony_ci .vidioc_s_input = cio2_video_s_input, 11838c2ecf20Sopenharmony_ci}; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_cistatic int cio2_subdev_subscribe_event(struct v4l2_subdev *sd, 11868c2ecf20Sopenharmony_ci struct v4l2_fh *fh, 11878c2ecf20Sopenharmony_ci struct v4l2_event_subscription *sub) 11888c2ecf20Sopenharmony_ci{ 11898c2ecf20Sopenharmony_ci if (sub->type != V4L2_EVENT_FRAME_SYNC) 11908c2ecf20Sopenharmony_ci return -EINVAL; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci /* Line number. For now only zero accepted. */ 11938c2ecf20Sopenharmony_ci if (sub->id != 0) 11948c2ecf20Sopenharmony_ci return -EINVAL; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci return v4l2_event_subscribe(fh, sub, 0, NULL); 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic int cio2_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 12028c2ecf20Sopenharmony_ci const struct v4l2_mbus_framefmt fmt_default = { 12038c2ecf20Sopenharmony_ci .width = 1936, 12048c2ecf20Sopenharmony_ci .height = 1096, 12058c2ecf20Sopenharmony_ci .code = formats[0].mbus_code, 12068c2ecf20Sopenharmony_ci .field = V4L2_FIELD_NONE, 12078c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_RAW, 12088c2ecf20Sopenharmony_ci .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, 12098c2ecf20Sopenharmony_ci .quantization = V4L2_QUANTIZATION_DEFAULT, 12108c2ecf20Sopenharmony_ci .xfer_func = V4L2_XFER_FUNC_DEFAULT, 12118c2ecf20Sopenharmony_ci }; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* Initialize try_fmt */ 12148c2ecf20Sopenharmony_ci format = v4l2_subdev_get_try_format(sd, fh->pad, CIO2_PAD_SINK); 12158c2ecf20Sopenharmony_ci *format = fmt_default; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci /* same as sink */ 12188c2ecf20Sopenharmony_ci format = v4l2_subdev_get_try_format(sd, fh->pad, CIO2_PAD_SOURCE); 12198c2ecf20Sopenharmony_ci *format = fmt_default; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci return 0; 12228c2ecf20Sopenharmony_ci} 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci/* 12258c2ecf20Sopenharmony_ci * cio2_subdev_get_fmt - Handle get format by pads subdev method 12268c2ecf20Sopenharmony_ci * @sd : pointer to v4l2 subdev structure 12278c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad config 12288c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure 12298c2ecf20Sopenharmony_ci * return -EINVAL or zero on success 12308c2ecf20Sopenharmony_ci */ 12318c2ecf20Sopenharmony_cistatic int cio2_subdev_get_fmt(struct v4l2_subdev *sd, 12328c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 12338c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci mutex_lock(&q->subdev_lock); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) 12408c2ecf20Sopenharmony_ci fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 12418c2ecf20Sopenharmony_ci else 12428c2ecf20Sopenharmony_ci fmt->format = q->subdev_fmt; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci mutex_unlock(&q->subdev_lock); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci return 0; 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci/* 12508c2ecf20Sopenharmony_ci * cio2_subdev_set_fmt - Handle set format by pads subdev method 12518c2ecf20Sopenharmony_ci * @sd : pointer to v4l2 subdev structure 12528c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad config 12538c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure 12548c2ecf20Sopenharmony_ci * return -EINVAL or zero on success 12558c2ecf20Sopenharmony_ci */ 12568c2ecf20Sopenharmony_cistatic int cio2_subdev_set_fmt(struct v4l2_subdev *sd, 12578c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 12588c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev); 12618c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mbus; 12628c2ecf20Sopenharmony_ci u32 mbus_code = fmt->format.code; 12638c2ecf20Sopenharmony_ci unsigned int i; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci /* 12668c2ecf20Sopenharmony_ci * Only allow setting sink pad format; 12678c2ecf20Sopenharmony_ci * source always propagates from sink 12688c2ecf20Sopenharmony_ci */ 12698c2ecf20Sopenharmony_ci if (fmt->pad == CIO2_PAD_SOURCE) 12708c2ecf20Sopenharmony_ci return cio2_subdev_get_fmt(sd, cfg, fmt); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) 12738c2ecf20Sopenharmony_ci mbus = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 12748c2ecf20Sopenharmony_ci else 12758c2ecf20Sopenharmony_ci mbus = &q->subdev_fmt; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci fmt->format.code = formats[0].mbus_code; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats); i++) { 12808c2ecf20Sopenharmony_ci if (formats[i].mbus_code == mbus_code) { 12818c2ecf20Sopenharmony_ci fmt->format.code = mbus_code; 12828c2ecf20Sopenharmony_ci break; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci fmt->format.width = min_t(u32, fmt->format.width, CIO2_IMAGE_MAX_WIDTH); 12878c2ecf20Sopenharmony_ci fmt->format.height = min_t(u32, fmt->format.height, 12888c2ecf20Sopenharmony_ci CIO2_IMAGE_MAX_LENGTH); 12898c2ecf20Sopenharmony_ci fmt->format.field = V4L2_FIELD_NONE; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci mutex_lock(&q->subdev_lock); 12928c2ecf20Sopenharmony_ci *mbus = fmt->format; 12938c2ecf20Sopenharmony_ci mutex_unlock(&q->subdev_lock); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci return 0; 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cistatic int cio2_subdev_enum_mbus_code(struct v4l2_subdev *sd, 12998c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 13008c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 13018c2ecf20Sopenharmony_ci{ 13028c2ecf20Sopenharmony_ci if (code->index >= ARRAY_SIZE(formats)) 13038c2ecf20Sopenharmony_ci return -EINVAL; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci code->code = formats[code->index].mbus_code; 13068c2ecf20Sopenharmony_ci return 0; 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_cistatic int cio2_subdev_link_validate_get_format(struct media_pad *pad, 13108c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 13118c2ecf20Sopenharmony_ci{ 13128c2ecf20Sopenharmony_ci if (is_media_entity_v4l2_subdev(pad->entity)) { 13138c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = 13148c2ecf20Sopenharmony_ci media_entity_to_v4l2_subdev(pad->entity); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; 13178c2ecf20Sopenharmony_ci fmt->pad = pad->index; 13188c2ecf20Sopenharmony_ci return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci return -EINVAL; 13228c2ecf20Sopenharmony_ci} 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_cistatic int cio2_video_link_validate(struct media_link *link) 13258c2ecf20Sopenharmony_ci{ 13268c2ecf20Sopenharmony_ci struct video_device *vd = container_of(link->sink->entity, 13278c2ecf20Sopenharmony_ci struct video_device, entity); 13288c2ecf20Sopenharmony_ci struct cio2_queue *q = container_of(vd, struct cio2_queue, vdev); 13298c2ecf20Sopenharmony_ci struct cio2_device *cio2 = video_get_drvdata(vd); 13308c2ecf20Sopenharmony_ci struct v4l2_subdev_format source_fmt; 13318c2ecf20Sopenharmony_ci int ret; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci if (!media_entity_remote_pad(link->sink->entity->pads)) { 13348c2ecf20Sopenharmony_ci dev_info(&cio2->pci_dev->dev, 13358c2ecf20Sopenharmony_ci "video node %s pad not connected\n", vd->name); 13368c2ecf20Sopenharmony_ci return -ENOTCONN; 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci ret = cio2_subdev_link_validate_get_format(link->source, &source_fmt); 13408c2ecf20Sopenharmony_ci if (ret < 0) 13418c2ecf20Sopenharmony_ci return 0; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci if (source_fmt.format.width != q->format.width || 13448c2ecf20Sopenharmony_ci source_fmt.format.height != q->format.height) { 13458c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, 13468c2ecf20Sopenharmony_ci "Wrong width or height %ux%u (%ux%u expected)\n", 13478c2ecf20Sopenharmony_ci q->format.width, q->format.height, 13488c2ecf20Sopenharmony_ci source_fmt.format.width, source_fmt.format.height); 13498c2ecf20Sopenharmony_ci return -EINVAL; 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci if (!cio2_find_format(&q->format.pixelformat, &source_fmt.format.code)) 13538c2ecf20Sopenharmony_ci return -EINVAL; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci return 0; 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops cio2_subdev_core_ops = { 13598c2ecf20Sopenharmony_ci .subscribe_event = cio2_subdev_subscribe_event, 13608c2ecf20Sopenharmony_ci .unsubscribe_event = v4l2_event_subdev_unsubscribe, 13618c2ecf20Sopenharmony_ci}; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops cio2_subdev_internal_ops = { 13648c2ecf20Sopenharmony_ci .open = cio2_subdev_open, 13658c2ecf20Sopenharmony_ci}; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops cio2_subdev_pad_ops = { 13688c2ecf20Sopenharmony_ci .link_validate = v4l2_subdev_link_validate_default, 13698c2ecf20Sopenharmony_ci .get_fmt = cio2_subdev_get_fmt, 13708c2ecf20Sopenharmony_ci .set_fmt = cio2_subdev_set_fmt, 13718c2ecf20Sopenharmony_ci .enum_mbus_code = cio2_subdev_enum_mbus_code, 13728c2ecf20Sopenharmony_ci}; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops cio2_subdev_ops = { 13758c2ecf20Sopenharmony_ci .core = &cio2_subdev_core_ops, 13768c2ecf20Sopenharmony_ci .pad = &cio2_subdev_pad_ops, 13778c2ecf20Sopenharmony_ci}; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci/******* V4L2 sub-device asynchronous registration callbacks***********/ 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_cistruct sensor_async_subdev { 13828c2ecf20Sopenharmony_ci struct v4l2_async_subdev asd; 13838c2ecf20Sopenharmony_ci struct csi2_bus_info csi2; 13848c2ecf20Sopenharmony_ci}; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci/* The .bound() notifier callback when a match is found */ 13878c2ecf20Sopenharmony_cistatic int cio2_notifier_bound(struct v4l2_async_notifier *notifier, 13888c2ecf20Sopenharmony_ci struct v4l2_subdev *sd, 13898c2ecf20Sopenharmony_ci struct v4l2_async_subdev *asd) 13908c2ecf20Sopenharmony_ci{ 13918c2ecf20Sopenharmony_ci struct cio2_device *cio2 = container_of(notifier, 13928c2ecf20Sopenharmony_ci struct cio2_device, notifier); 13938c2ecf20Sopenharmony_ci struct sensor_async_subdev *s_asd = container_of(asd, 13948c2ecf20Sopenharmony_ci struct sensor_async_subdev, asd); 13958c2ecf20Sopenharmony_ci struct cio2_queue *q; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (cio2->queue[s_asd->csi2.port].sensor) 13988c2ecf20Sopenharmony_ci return -EBUSY; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci q = &cio2->queue[s_asd->csi2.port]; 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci q->csi2 = s_asd->csi2; 14038c2ecf20Sopenharmony_ci q->sensor = sd; 14048c2ecf20Sopenharmony_ci q->csi_rx_base = cio2->base + CIO2_REG_PIPE_BASE(q->csi2.port); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci return 0; 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci/* The .unbind callback */ 14108c2ecf20Sopenharmony_cistatic void cio2_notifier_unbind(struct v4l2_async_notifier *notifier, 14118c2ecf20Sopenharmony_ci struct v4l2_subdev *sd, 14128c2ecf20Sopenharmony_ci struct v4l2_async_subdev *asd) 14138c2ecf20Sopenharmony_ci{ 14148c2ecf20Sopenharmony_ci struct cio2_device *cio2 = container_of(notifier, 14158c2ecf20Sopenharmony_ci struct cio2_device, notifier); 14168c2ecf20Sopenharmony_ci struct sensor_async_subdev *s_asd = container_of(asd, 14178c2ecf20Sopenharmony_ci struct sensor_async_subdev, asd); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci cio2->queue[s_asd->csi2.port].sensor = NULL; 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci/* .complete() is called after all subdevices have been located */ 14238c2ecf20Sopenharmony_cistatic int cio2_notifier_complete(struct v4l2_async_notifier *notifier) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci struct cio2_device *cio2 = container_of(notifier, struct cio2_device, 14268c2ecf20Sopenharmony_ci notifier); 14278c2ecf20Sopenharmony_ci struct sensor_async_subdev *s_asd; 14288c2ecf20Sopenharmony_ci struct v4l2_async_subdev *asd; 14298c2ecf20Sopenharmony_ci struct cio2_queue *q; 14308c2ecf20Sopenharmony_ci unsigned int pad; 14318c2ecf20Sopenharmony_ci int ret; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci list_for_each_entry(asd, &cio2->notifier.asd_list, asd_list) { 14348c2ecf20Sopenharmony_ci s_asd = container_of(asd, struct sensor_async_subdev, asd); 14358c2ecf20Sopenharmony_ci q = &cio2->queue[s_asd->csi2.port]; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci for (pad = 0; pad < q->sensor->entity.num_pads; pad++) 14388c2ecf20Sopenharmony_ci if (q->sensor->entity.pads[pad].flags & 14398c2ecf20Sopenharmony_ci MEDIA_PAD_FL_SOURCE) 14408c2ecf20Sopenharmony_ci break; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci if (pad == q->sensor->entity.num_pads) { 14438c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, 14448c2ecf20Sopenharmony_ci "failed to find src pad for %s\n", 14458c2ecf20Sopenharmony_ci q->sensor->name); 14468c2ecf20Sopenharmony_ci return -ENXIO; 14478c2ecf20Sopenharmony_ci } 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci ret = media_create_pad_link( 14508c2ecf20Sopenharmony_ci &q->sensor->entity, pad, 14518c2ecf20Sopenharmony_ci &q->subdev.entity, CIO2_PAD_SINK, 14528c2ecf20Sopenharmony_ci 0); 14538c2ecf20Sopenharmony_ci if (ret) { 14548c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, 14558c2ecf20Sopenharmony_ci "failed to create link for %s\n", 14568c2ecf20Sopenharmony_ci q->sensor->name); 14578c2ecf20Sopenharmony_ci return ret; 14588c2ecf20Sopenharmony_ci } 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci return v4l2_device_register_subdev_nodes(&cio2->v4l2_dev); 14628c2ecf20Sopenharmony_ci} 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_cistatic const struct v4l2_async_notifier_operations cio2_async_ops = { 14658c2ecf20Sopenharmony_ci .bound = cio2_notifier_bound, 14668c2ecf20Sopenharmony_ci .unbind = cio2_notifier_unbind, 14678c2ecf20Sopenharmony_ci .complete = cio2_notifier_complete, 14688c2ecf20Sopenharmony_ci}; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_cistatic int cio2_parse_firmware(struct cio2_device *cio2) 14718c2ecf20Sopenharmony_ci{ 14728c2ecf20Sopenharmony_ci unsigned int i; 14738c2ecf20Sopenharmony_ci int ret; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci for (i = 0; i < CIO2_NUM_PORTS; i++) { 14768c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint vep = { 14778c2ecf20Sopenharmony_ci .bus_type = V4L2_MBUS_CSI2_DPHY 14788c2ecf20Sopenharmony_ci }; 14798c2ecf20Sopenharmony_ci struct sensor_async_subdev *s_asd; 14808c2ecf20Sopenharmony_ci struct v4l2_async_subdev *asd; 14818c2ecf20Sopenharmony_ci struct fwnode_handle *ep; 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci ep = fwnode_graph_get_endpoint_by_id( 14848c2ecf20Sopenharmony_ci dev_fwnode(&cio2->pci_dev->dev), i, 0, 14858c2ecf20Sopenharmony_ci FWNODE_GRAPH_ENDPOINT_NEXT); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci if (!ep) 14888c2ecf20Sopenharmony_ci continue; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(ep, &vep); 14918c2ecf20Sopenharmony_ci if (ret) 14928c2ecf20Sopenharmony_ci goto err_parse; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci asd = v4l2_async_notifier_add_fwnode_remote_subdev( 14958c2ecf20Sopenharmony_ci &cio2->notifier, ep, sizeof(*s_asd)); 14968c2ecf20Sopenharmony_ci if (IS_ERR(asd)) { 14978c2ecf20Sopenharmony_ci ret = PTR_ERR(asd); 14988c2ecf20Sopenharmony_ci goto err_parse; 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci s_asd = container_of(asd, struct sensor_async_subdev, asd); 15028c2ecf20Sopenharmony_ci s_asd->csi2.port = vep.base.port; 15038c2ecf20Sopenharmony_ci s_asd->csi2.lanes = vep.bus.mipi_csi2.num_data_lanes; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci fwnode_handle_put(ep); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci continue; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_cierr_parse: 15108c2ecf20Sopenharmony_ci fwnode_handle_put(ep); 15118c2ecf20Sopenharmony_ci return ret; 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci /* 15158c2ecf20Sopenharmony_ci * Proceed even without sensors connected to allow the device to 15168c2ecf20Sopenharmony_ci * suspend. 15178c2ecf20Sopenharmony_ci */ 15188c2ecf20Sopenharmony_ci cio2->notifier.ops = &cio2_async_ops; 15198c2ecf20Sopenharmony_ci ret = v4l2_async_notifier_register(&cio2->v4l2_dev, &cio2->notifier); 15208c2ecf20Sopenharmony_ci if (ret) 15218c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, 15228c2ecf20Sopenharmony_ci "failed to register async notifier : %d\n", ret); 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci return ret; 15258c2ecf20Sopenharmony_ci} 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci/**************** Queue initialization ****************/ 15288c2ecf20Sopenharmony_cistatic const struct media_entity_operations cio2_media_ops = { 15298c2ecf20Sopenharmony_ci .link_validate = v4l2_subdev_link_validate, 15308c2ecf20Sopenharmony_ci}; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic const struct media_entity_operations cio2_video_entity_ops = { 15338c2ecf20Sopenharmony_ci .link_validate = cio2_video_link_validate, 15348c2ecf20Sopenharmony_ci}; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_cistatic int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q) 15378c2ecf20Sopenharmony_ci{ 15388c2ecf20Sopenharmony_ci static const u32 default_width = 1936; 15398c2ecf20Sopenharmony_ci static const u32 default_height = 1096; 15408c2ecf20Sopenharmony_ci const struct ipu3_cio2_fmt dflt_fmt = formats[0]; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci struct video_device *vdev = &q->vdev; 15438c2ecf20Sopenharmony_ci struct vb2_queue *vbq = &q->vbq; 15448c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev = &q->subdev; 15458c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt; 15468c2ecf20Sopenharmony_ci int r; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci /* Initialize miscellaneous variables */ 15498c2ecf20Sopenharmony_ci mutex_init(&q->lock); 15508c2ecf20Sopenharmony_ci mutex_init(&q->subdev_lock); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci /* Initialize formats to default values */ 15538c2ecf20Sopenharmony_ci fmt = &q->subdev_fmt; 15548c2ecf20Sopenharmony_ci fmt->width = default_width; 15558c2ecf20Sopenharmony_ci fmt->height = default_height; 15568c2ecf20Sopenharmony_ci fmt->code = dflt_fmt.mbus_code; 15578c2ecf20Sopenharmony_ci fmt->field = V4L2_FIELD_NONE; 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci q->format.width = default_width; 15608c2ecf20Sopenharmony_ci q->format.height = default_height; 15618c2ecf20Sopenharmony_ci q->format.pixelformat = dflt_fmt.fourcc; 15628c2ecf20Sopenharmony_ci q->format.colorspace = V4L2_COLORSPACE_RAW; 15638c2ecf20Sopenharmony_ci q->format.field = V4L2_FIELD_NONE; 15648c2ecf20Sopenharmony_ci q->format.num_planes = 1; 15658c2ecf20Sopenharmony_ci q->format.plane_fmt[0].bytesperline = 15668c2ecf20Sopenharmony_ci cio2_bytesperline(q->format.width); 15678c2ecf20Sopenharmony_ci q->format.plane_fmt[0].sizeimage = q->format.plane_fmt[0].bytesperline * 15688c2ecf20Sopenharmony_ci q->format.height; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci /* Initialize fbpt */ 15718c2ecf20Sopenharmony_ci r = cio2_fbpt_init(cio2, q); 15728c2ecf20Sopenharmony_ci if (r) 15738c2ecf20Sopenharmony_ci goto fail_fbpt; 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci /* Initialize media entities */ 15768c2ecf20Sopenharmony_ci q->subdev_pads[CIO2_PAD_SINK].flags = MEDIA_PAD_FL_SINK | 15778c2ecf20Sopenharmony_ci MEDIA_PAD_FL_MUST_CONNECT; 15788c2ecf20Sopenharmony_ci q->subdev_pads[CIO2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 15798c2ecf20Sopenharmony_ci subdev->entity.ops = &cio2_media_ops; 15808c2ecf20Sopenharmony_ci subdev->internal_ops = &cio2_subdev_internal_ops; 15818c2ecf20Sopenharmony_ci r = media_entity_pads_init(&subdev->entity, CIO2_PADS, q->subdev_pads); 15828c2ecf20Sopenharmony_ci if (r) { 15838c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, 15848c2ecf20Sopenharmony_ci "failed initialize subdev media entity (%d)\n", r); 15858c2ecf20Sopenharmony_ci goto fail_subdev_media_entity; 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci q->vdev_pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; 15898c2ecf20Sopenharmony_ci vdev->entity.ops = &cio2_video_entity_ops; 15908c2ecf20Sopenharmony_ci r = media_entity_pads_init(&vdev->entity, 1, &q->vdev_pad); 15918c2ecf20Sopenharmony_ci if (r) { 15928c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, 15938c2ecf20Sopenharmony_ci "failed initialize videodev media entity (%d)\n", r); 15948c2ecf20Sopenharmony_ci goto fail_vdev_media_entity; 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci /* Initialize subdev */ 15988c2ecf20Sopenharmony_ci v4l2_subdev_init(subdev, &cio2_subdev_ops); 15998c2ecf20Sopenharmony_ci subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; 16008c2ecf20Sopenharmony_ci subdev->owner = THIS_MODULE; 16018c2ecf20Sopenharmony_ci snprintf(subdev->name, sizeof(subdev->name), 16028c2ecf20Sopenharmony_ci CIO2_ENTITY_NAME " %td", q - cio2->queue); 16038c2ecf20Sopenharmony_ci subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 16048c2ecf20Sopenharmony_ci v4l2_set_subdevdata(subdev, cio2); 16058c2ecf20Sopenharmony_ci r = v4l2_device_register_subdev(&cio2->v4l2_dev, subdev); 16068c2ecf20Sopenharmony_ci if (r) { 16078c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, 16088c2ecf20Sopenharmony_ci "failed initialize subdev (%d)\n", r); 16098c2ecf20Sopenharmony_ci goto fail_subdev; 16108c2ecf20Sopenharmony_ci } 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci /* Initialize vbq */ 16138c2ecf20Sopenharmony_ci vbq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 16148c2ecf20Sopenharmony_ci vbq->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF; 16158c2ecf20Sopenharmony_ci vbq->ops = &cio2_vb2_ops; 16168c2ecf20Sopenharmony_ci vbq->mem_ops = &vb2_dma_sg_memops; 16178c2ecf20Sopenharmony_ci vbq->buf_struct_size = sizeof(struct cio2_buffer); 16188c2ecf20Sopenharmony_ci vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 16198c2ecf20Sopenharmony_ci vbq->min_buffers_needed = 1; 16208c2ecf20Sopenharmony_ci vbq->drv_priv = cio2; 16218c2ecf20Sopenharmony_ci vbq->lock = &q->lock; 16228c2ecf20Sopenharmony_ci r = vb2_queue_init(vbq); 16238c2ecf20Sopenharmony_ci if (r) { 16248c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, 16258c2ecf20Sopenharmony_ci "failed to initialize videobuf2 queue (%d)\n", r); 16268c2ecf20Sopenharmony_ci goto fail_subdev; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* Initialize vdev */ 16308c2ecf20Sopenharmony_ci snprintf(vdev->name, sizeof(vdev->name), 16318c2ecf20Sopenharmony_ci "%s %td", CIO2_NAME, q - cio2->queue); 16328c2ecf20Sopenharmony_ci vdev->release = video_device_release_empty; 16338c2ecf20Sopenharmony_ci vdev->fops = &cio2_v4l2_fops; 16348c2ecf20Sopenharmony_ci vdev->ioctl_ops = &cio2_v4l2_ioctl_ops; 16358c2ecf20Sopenharmony_ci vdev->lock = &cio2->lock; 16368c2ecf20Sopenharmony_ci vdev->v4l2_dev = &cio2->v4l2_dev; 16378c2ecf20Sopenharmony_ci vdev->queue = &q->vbq; 16388c2ecf20Sopenharmony_ci vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; 16398c2ecf20Sopenharmony_ci video_set_drvdata(vdev, cio2); 16408c2ecf20Sopenharmony_ci r = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 16418c2ecf20Sopenharmony_ci if (r) { 16428c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, 16438c2ecf20Sopenharmony_ci "failed to register video device (%d)\n", r); 16448c2ecf20Sopenharmony_ci goto fail_vdev; 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci /* Create link from CIO2 subdev to output node */ 16488c2ecf20Sopenharmony_ci r = media_create_pad_link( 16498c2ecf20Sopenharmony_ci &subdev->entity, CIO2_PAD_SOURCE, &vdev->entity, 0, 16508c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); 16518c2ecf20Sopenharmony_ci if (r) 16528c2ecf20Sopenharmony_ci goto fail_link; 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci return 0; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_cifail_link: 16578c2ecf20Sopenharmony_ci vb2_video_unregister_device(&q->vdev); 16588c2ecf20Sopenharmony_cifail_vdev: 16598c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(subdev); 16608c2ecf20Sopenharmony_cifail_subdev: 16618c2ecf20Sopenharmony_ci media_entity_cleanup(&vdev->entity); 16628c2ecf20Sopenharmony_cifail_vdev_media_entity: 16638c2ecf20Sopenharmony_ci media_entity_cleanup(&subdev->entity); 16648c2ecf20Sopenharmony_cifail_subdev_media_entity: 16658c2ecf20Sopenharmony_ci cio2_fbpt_exit(q, &cio2->pci_dev->dev); 16668c2ecf20Sopenharmony_cifail_fbpt: 16678c2ecf20Sopenharmony_ci mutex_destroy(&q->subdev_lock); 16688c2ecf20Sopenharmony_ci mutex_destroy(&q->lock); 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci return r; 16718c2ecf20Sopenharmony_ci} 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_cistatic void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q) 16748c2ecf20Sopenharmony_ci{ 16758c2ecf20Sopenharmony_ci vb2_video_unregister_device(&q->vdev); 16768c2ecf20Sopenharmony_ci media_entity_cleanup(&q->vdev.entity); 16778c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(&q->subdev); 16788c2ecf20Sopenharmony_ci media_entity_cleanup(&q->subdev.entity); 16798c2ecf20Sopenharmony_ci cio2_fbpt_exit(q, &cio2->pci_dev->dev); 16808c2ecf20Sopenharmony_ci mutex_destroy(&q->subdev_lock); 16818c2ecf20Sopenharmony_ci mutex_destroy(&q->lock); 16828c2ecf20Sopenharmony_ci} 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_cistatic int cio2_queues_init(struct cio2_device *cio2) 16858c2ecf20Sopenharmony_ci{ 16868c2ecf20Sopenharmony_ci int i, r; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci for (i = 0; i < CIO2_QUEUES; i++) { 16898c2ecf20Sopenharmony_ci r = cio2_queue_init(cio2, &cio2->queue[i]); 16908c2ecf20Sopenharmony_ci if (r) 16918c2ecf20Sopenharmony_ci break; 16928c2ecf20Sopenharmony_ci } 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci if (i == CIO2_QUEUES) 16958c2ecf20Sopenharmony_ci return 0; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) 16988c2ecf20Sopenharmony_ci cio2_queue_exit(cio2, &cio2->queue[i]); 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci return r; 17018c2ecf20Sopenharmony_ci} 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_cistatic void cio2_queues_exit(struct cio2_device *cio2) 17048c2ecf20Sopenharmony_ci{ 17058c2ecf20Sopenharmony_ci unsigned int i; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci for (i = 0; i < CIO2_QUEUES; i++) 17088c2ecf20Sopenharmony_ci cio2_queue_exit(cio2, &cio2->queue[i]); 17098c2ecf20Sopenharmony_ci} 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci/**************** PCI interface ****************/ 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_cistatic int cio2_pci_probe(struct pci_dev *pci_dev, 17148c2ecf20Sopenharmony_ci const struct pci_device_id *id) 17158c2ecf20Sopenharmony_ci{ 17168c2ecf20Sopenharmony_ci struct cio2_device *cio2; 17178c2ecf20Sopenharmony_ci int r; 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci cio2 = devm_kzalloc(&pci_dev->dev, sizeof(*cio2), GFP_KERNEL); 17208c2ecf20Sopenharmony_ci if (!cio2) 17218c2ecf20Sopenharmony_ci return -ENOMEM; 17228c2ecf20Sopenharmony_ci cio2->pci_dev = pci_dev; 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci r = pcim_enable_device(pci_dev); 17258c2ecf20Sopenharmony_ci if (r) { 17268c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, "failed to enable device (%d)\n", r); 17278c2ecf20Sopenharmony_ci return r; 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci dev_info(&pci_dev->dev, "device 0x%x (rev: 0x%x)\n", 17318c2ecf20Sopenharmony_ci pci_dev->device, pci_dev->revision); 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci r = pcim_iomap_regions(pci_dev, 1 << CIO2_PCI_BAR, pci_name(pci_dev)); 17348c2ecf20Sopenharmony_ci if (r) { 17358c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, "failed to remap I/O memory (%d)\n", r); 17368c2ecf20Sopenharmony_ci return -ENODEV; 17378c2ecf20Sopenharmony_ci } 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci cio2->base = pcim_iomap_table(pci_dev)[CIO2_PCI_BAR]; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci pci_set_drvdata(pci_dev, cio2); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci pci_set_master(pci_dev); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci r = pci_set_dma_mask(pci_dev, CIO2_DMA_MASK); 17468c2ecf20Sopenharmony_ci if (r) { 17478c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, "failed to set DMA mask (%d)\n", r); 17488c2ecf20Sopenharmony_ci return -ENODEV; 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci r = pci_enable_msi(pci_dev); 17528c2ecf20Sopenharmony_ci if (r) { 17538c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, "failed to enable MSI (%d)\n", r); 17548c2ecf20Sopenharmony_ci return r; 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci r = cio2_fbpt_init_dummy(cio2); 17588c2ecf20Sopenharmony_ci if (r) 17598c2ecf20Sopenharmony_ci return r; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci mutex_init(&cio2->lock); 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci cio2->media_dev.dev = &cio2->pci_dev->dev; 17648c2ecf20Sopenharmony_ci strscpy(cio2->media_dev.model, CIO2_DEVICE_NAME, 17658c2ecf20Sopenharmony_ci sizeof(cio2->media_dev.model)); 17668c2ecf20Sopenharmony_ci snprintf(cio2->media_dev.bus_info, sizeof(cio2->media_dev.bus_info), 17678c2ecf20Sopenharmony_ci "PCI:%s", pci_name(cio2->pci_dev)); 17688c2ecf20Sopenharmony_ci cio2->media_dev.hw_revision = 0; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci media_device_init(&cio2->media_dev); 17718c2ecf20Sopenharmony_ci r = media_device_register(&cio2->media_dev); 17728c2ecf20Sopenharmony_ci if (r < 0) 17738c2ecf20Sopenharmony_ci goto fail_mutex_destroy; 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci cio2->v4l2_dev.mdev = &cio2->media_dev; 17768c2ecf20Sopenharmony_ci r = v4l2_device_register(&pci_dev->dev, &cio2->v4l2_dev); 17778c2ecf20Sopenharmony_ci if (r) { 17788c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, 17798c2ecf20Sopenharmony_ci "failed to register V4L2 device (%d)\n", r); 17808c2ecf20Sopenharmony_ci goto fail_media_device_unregister; 17818c2ecf20Sopenharmony_ci } 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci r = cio2_queues_init(cio2); 17848c2ecf20Sopenharmony_ci if (r) 17858c2ecf20Sopenharmony_ci goto fail_v4l2_device_unregister; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci v4l2_async_notifier_init(&cio2->notifier); 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci /* Register notifier for subdevices we care */ 17908c2ecf20Sopenharmony_ci r = cio2_parse_firmware(cio2); 17918c2ecf20Sopenharmony_ci if (r) 17928c2ecf20Sopenharmony_ci goto fail_clean_notifier; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci r = devm_request_irq(&pci_dev->dev, pci_dev->irq, cio2_irq, 17958c2ecf20Sopenharmony_ci IRQF_SHARED, CIO2_NAME, cio2); 17968c2ecf20Sopenharmony_ci if (r) { 17978c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, "failed to request IRQ (%d)\n", r); 17988c2ecf20Sopenharmony_ci goto fail_clean_notifier; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&pci_dev->dev); 18028c2ecf20Sopenharmony_ci pm_runtime_allow(&pci_dev->dev); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci return 0; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_cifail_clean_notifier: 18078c2ecf20Sopenharmony_ci v4l2_async_notifier_unregister(&cio2->notifier); 18088c2ecf20Sopenharmony_ci v4l2_async_notifier_cleanup(&cio2->notifier); 18098c2ecf20Sopenharmony_ci cio2_queues_exit(cio2); 18108c2ecf20Sopenharmony_cifail_v4l2_device_unregister: 18118c2ecf20Sopenharmony_ci v4l2_device_unregister(&cio2->v4l2_dev); 18128c2ecf20Sopenharmony_cifail_media_device_unregister: 18138c2ecf20Sopenharmony_ci media_device_unregister(&cio2->media_dev); 18148c2ecf20Sopenharmony_ci media_device_cleanup(&cio2->media_dev); 18158c2ecf20Sopenharmony_cifail_mutex_destroy: 18168c2ecf20Sopenharmony_ci mutex_destroy(&cio2->lock); 18178c2ecf20Sopenharmony_ci cio2_fbpt_exit_dummy(cio2); 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci return r; 18208c2ecf20Sopenharmony_ci} 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_cistatic void cio2_pci_remove(struct pci_dev *pci_dev) 18238c2ecf20Sopenharmony_ci{ 18248c2ecf20Sopenharmony_ci struct cio2_device *cio2 = pci_get_drvdata(pci_dev); 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci media_device_unregister(&cio2->media_dev); 18278c2ecf20Sopenharmony_ci v4l2_async_notifier_unregister(&cio2->notifier); 18288c2ecf20Sopenharmony_ci v4l2_async_notifier_cleanup(&cio2->notifier); 18298c2ecf20Sopenharmony_ci cio2_queues_exit(cio2); 18308c2ecf20Sopenharmony_ci cio2_fbpt_exit_dummy(cio2); 18318c2ecf20Sopenharmony_ci v4l2_device_unregister(&cio2->v4l2_dev); 18328c2ecf20Sopenharmony_ci media_device_cleanup(&cio2->media_dev); 18338c2ecf20Sopenharmony_ci mutex_destroy(&cio2->lock); 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci pm_runtime_forbid(&pci_dev->dev); 18368c2ecf20Sopenharmony_ci pm_runtime_get_noresume(&pci_dev->dev); 18378c2ecf20Sopenharmony_ci} 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_cistatic int __maybe_unused cio2_runtime_suspend(struct device *dev) 18408c2ecf20Sopenharmony_ci{ 18418c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 18428c2ecf20Sopenharmony_ci struct cio2_device *cio2 = pci_get_drvdata(pci_dev); 18438c2ecf20Sopenharmony_ci void __iomem *const base = cio2->base; 18448c2ecf20Sopenharmony_ci u16 pm; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci writel(CIO2_D0I3C_I3, base + CIO2_REG_D0I3C); 18478c2ecf20Sopenharmony_ci dev_dbg(dev, "cio2 runtime suspend.\n"); 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm); 18508c2ecf20Sopenharmony_ci pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT; 18518c2ecf20Sopenharmony_ci pm |= CIO2_PMCSR_D3; 18528c2ecf20Sopenharmony_ci pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm); 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci return 0; 18558c2ecf20Sopenharmony_ci} 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_cistatic int __maybe_unused cio2_runtime_resume(struct device *dev) 18588c2ecf20Sopenharmony_ci{ 18598c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 18608c2ecf20Sopenharmony_ci struct cio2_device *cio2 = pci_get_drvdata(pci_dev); 18618c2ecf20Sopenharmony_ci void __iomem *const base = cio2->base; 18628c2ecf20Sopenharmony_ci u16 pm; 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci writel(CIO2_D0I3C_RR, base + CIO2_REG_D0I3C); 18658c2ecf20Sopenharmony_ci dev_dbg(dev, "cio2 runtime resume.\n"); 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm); 18688c2ecf20Sopenharmony_ci pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT; 18698c2ecf20Sopenharmony_ci pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm); 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci return 0; 18728c2ecf20Sopenharmony_ci} 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci/* 18758c2ecf20Sopenharmony_ci * Helper function to advance all the elements of a circular buffer by "start" 18768c2ecf20Sopenharmony_ci * positions 18778c2ecf20Sopenharmony_ci */ 18788c2ecf20Sopenharmony_cistatic void arrange(void *ptr, size_t elem_size, size_t elems, size_t start) 18798c2ecf20Sopenharmony_ci{ 18808c2ecf20Sopenharmony_ci struct { 18818c2ecf20Sopenharmony_ci size_t begin, end; 18828c2ecf20Sopenharmony_ci } arr[2] = { 18838c2ecf20Sopenharmony_ci { 0, start - 1 }, 18848c2ecf20Sopenharmony_ci { start, elems - 1 }, 18858c2ecf20Sopenharmony_ci }; 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci#define CHUNK_SIZE(a) ((a)->end - (a)->begin + 1) 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci /* Loop as long as we have out-of-place entries */ 18908c2ecf20Sopenharmony_ci while (CHUNK_SIZE(&arr[0]) && CHUNK_SIZE(&arr[1])) { 18918c2ecf20Sopenharmony_ci size_t size0, i; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci /* 18948c2ecf20Sopenharmony_ci * Find the number of entries that can be arranged on this 18958c2ecf20Sopenharmony_ci * iteration. 18968c2ecf20Sopenharmony_ci */ 18978c2ecf20Sopenharmony_ci size0 = min(CHUNK_SIZE(&arr[0]), CHUNK_SIZE(&arr[1])); 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci /* Swap the entries in two parts of the array. */ 19008c2ecf20Sopenharmony_ci for (i = 0; i < size0; i++) { 19018c2ecf20Sopenharmony_ci u8 *d = ptr + elem_size * (arr[1].begin + i); 19028c2ecf20Sopenharmony_ci u8 *s = ptr + elem_size * (arr[0].begin + i); 19038c2ecf20Sopenharmony_ci size_t j; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci for (j = 0; j < elem_size; j++) 19068c2ecf20Sopenharmony_ci swap(d[j], s[j]); 19078c2ecf20Sopenharmony_ci } 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci if (CHUNK_SIZE(&arr[0]) > CHUNK_SIZE(&arr[1])) { 19108c2ecf20Sopenharmony_ci /* The end of the first array remains unarranged. */ 19118c2ecf20Sopenharmony_ci arr[0].begin += size0; 19128c2ecf20Sopenharmony_ci } else { 19138c2ecf20Sopenharmony_ci /* 19148c2ecf20Sopenharmony_ci * The first array is fully arranged so we proceed 19158c2ecf20Sopenharmony_ci * handling the next one. 19168c2ecf20Sopenharmony_ci */ 19178c2ecf20Sopenharmony_ci arr[0].begin = arr[1].begin; 19188c2ecf20Sopenharmony_ci arr[0].end = arr[1].begin + size0 - 1; 19198c2ecf20Sopenharmony_ci arr[1].begin += size0; 19208c2ecf20Sopenharmony_ci } 19218c2ecf20Sopenharmony_ci } 19228c2ecf20Sopenharmony_ci} 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_cistatic void cio2_fbpt_rearrange(struct cio2_device *cio2, struct cio2_queue *q) 19258c2ecf20Sopenharmony_ci{ 19268c2ecf20Sopenharmony_ci unsigned int i, j; 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci for (i = 0, j = q->bufs_first; i < CIO2_MAX_BUFFERS; 19298c2ecf20Sopenharmony_ci i++, j = (j + 1) % CIO2_MAX_BUFFERS) 19308c2ecf20Sopenharmony_ci if (q->bufs[j]) 19318c2ecf20Sopenharmony_ci break; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci if (i == CIO2_MAX_BUFFERS) 19348c2ecf20Sopenharmony_ci return; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci if (j) { 19378c2ecf20Sopenharmony_ci arrange(q->fbpt, sizeof(struct cio2_fbpt_entry) * CIO2_MAX_LOPS, 19388c2ecf20Sopenharmony_ci CIO2_MAX_BUFFERS, j); 19398c2ecf20Sopenharmony_ci arrange(q->bufs, sizeof(struct cio2_buffer *), 19408c2ecf20Sopenharmony_ci CIO2_MAX_BUFFERS, j); 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci /* 19448c2ecf20Sopenharmony_ci * DMA clears the valid bit when accessing the buffer. 19458c2ecf20Sopenharmony_ci * When stopping stream in suspend callback, some of the buffers 19468c2ecf20Sopenharmony_ci * may be in invalid state. After resume, when DMA meets the invalid 19478c2ecf20Sopenharmony_ci * buffer, it will halt and stop receiving new data. 19488c2ecf20Sopenharmony_ci * To avoid DMA halting, set the valid bit for all buffers in FBPT. 19498c2ecf20Sopenharmony_ci */ 19508c2ecf20Sopenharmony_ci for (i = 0; i < CIO2_MAX_BUFFERS; i++) 19518c2ecf20Sopenharmony_ci cio2_fbpt_entry_enable(cio2, q->fbpt + i * CIO2_MAX_LOPS); 19528c2ecf20Sopenharmony_ci} 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_cistatic int __maybe_unused cio2_suspend(struct device *dev) 19558c2ecf20Sopenharmony_ci{ 19568c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 19578c2ecf20Sopenharmony_ci struct cio2_device *cio2 = pci_get_drvdata(pci_dev); 19588c2ecf20Sopenharmony_ci struct cio2_queue *q = cio2->cur_queue; 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci dev_dbg(dev, "cio2 suspend\n"); 19618c2ecf20Sopenharmony_ci if (!cio2->streaming) 19628c2ecf20Sopenharmony_ci return 0; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci /* Stop stream */ 19658c2ecf20Sopenharmony_ci cio2_hw_exit(cio2, q); 19668c2ecf20Sopenharmony_ci synchronize_irq(pci_dev->irq); 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci pm_runtime_force_suspend(dev); 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci /* 19718c2ecf20Sopenharmony_ci * Upon resume, hw starts to process the fbpt entries from beginning, 19728c2ecf20Sopenharmony_ci * so relocate the queued buffs to the fbpt head before suspend. 19738c2ecf20Sopenharmony_ci */ 19748c2ecf20Sopenharmony_ci cio2_fbpt_rearrange(cio2, q); 19758c2ecf20Sopenharmony_ci q->bufs_first = 0; 19768c2ecf20Sopenharmony_ci q->bufs_next = 0; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci return 0; 19798c2ecf20Sopenharmony_ci} 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_cistatic int __maybe_unused cio2_resume(struct device *dev) 19828c2ecf20Sopenharmony_ci{ 19838c2ecf20Sopenharmony_ci struct cio2_device *cio2 = dev_get_drvdata(dev); 19848c2ecf20Sopenharmony_ci struct cio2_queue *q = cio2->cur_queue; 19858c2ecf20Sopenharmony_ci int r; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci dev_dbg(dev, "cio2 resume\n"); 19888c2ecf20Sopenharmony_ci if (!cio2->streaming) 19898c2ecf20Sopenharmony_ci return 0; 19908c2ecf20Sopenharmony_ci /* Start stream */ 19918c2ecf20Sopenharmony_ci r = pm_runtime_force_resume(&cio2->pci_dev->dev); 19928c2ecf20Sopenharmony_ci if (r < 0) { 19938c2ecf20Sopenharmony_ci dev_err(&cio2->pci_dev->dev, 19948c2ecf20Sopenharmony_ci "failed to set power %d\n", r); 19958c2ecf20Sopenharmony_ci return r; 19968c2ecf20Sopenharmony_ci } 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci r = cio2_hw_init(cio2, q); 19998c2ecf20Sopenharmony_ci if (r) 20008c2ecf20Sopenharmony_ci dev_err(dev, "fail to init cio2 hw\n"); 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci return r; 20038c2ecf20Sopenharmony_ci} 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_cistatic const struct dev_pm_ops cio2_pm_ops = { 20068c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(&cio2_runtime_suspend, &cio2_runtime_resume, NULL) 20078c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(&cio2_suspend, &cio2_resume) 20088c2ecf20Sopenharmony_ci}; 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_cistatic const struct pci_device_id cio2_pci_id_table[] = { 20118c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_INTEL, CIO2_PCI_ID) }, 20128c2ecf20Sopenharmony_ci { } 20138c2ecf20Sopenharmony_ci}; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cio2_pci_id_table); 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_cistatic struct pci_driver cio2_pci_driver = { 20188c2ecf20Sopenharmony_ci .name = CIO2_NAME, 20198c2ecf20Sopenharmony_ci .id_table = cio2_pci_id_table, 20208c2ecf20Sopenharmony_ci .probe = cio2_pci_probe, 20218c2ecf20Sopenharmony_ci .remove = cio2_pci_remove, 20228c2ecf20Sopenharmony_ci .driver = { 20238c2ecf20Sopenharmony_ci .pm = &cio2_pm_ops, 20248c2ecf20Sopenharmony_ci }, 20258c2ecf20Sopenharmony_ci}; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_cimodule_pci_driver(cio2_pci_driver); 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tuukka Toivonen <tuukka.toivonen@intel.com>"); 20308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); 20318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jian Xu Zheng"); 20328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>"); 20338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>"); 20348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 20358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IPU3 CIO2 driver"); 2036