1ec6859b2STodor Tomov /* 2ec6859b2STodor Tomov * camss-ispif.c 3ec6859b2STodor Tomov * 4ec6859b2STodor Tomov * Qualcomm MSM Camera Subsystem - ISPIF (ISP Interface) Module 5ec6859b2STodor Tomov * 6ec6859b2STodor Tomov * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. 7ec6859b2STodor Tomov * Copyright (C) 2015-2018 Linaro Ltd. 8ec6859b2STodor Tomov * 9ec6859b2STodor Tomov * This program is free software; you can redistribute it and/or modify 10ec6859b2STodor Tomov * it under the terms of the GNU General Public License version 2 and 11ec6859b2STodor Tomov * only version 2 as published by the Free Software Foundation. 12ec6859b2STodor Tomov * 13ec6859b2STodor Tomov * This program is distributed in the hope that it will be useful, 14ec6859b2STodor Tomov * but WITHOUT ANY WARRANTY; without even the implied warranty of 15ec6859b2STodor Tomov * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16ec6859b2STodor Tomov * GNU General Public License for more details. 17ec6859b2STodor Tomov */ 18ec6859b2STodor Tomov #include <linux/clk.h> 19ec6859b2STodor Tomov #include <linux/completion.h> 20ec6859b2STodor Tomov #include <linux/interrupt.h> 21ec6859b2STodor Tomov #include <linux/iopoll.h> 22ec6859b2STodor Tomov #include <linux/kernel.h> 23ec6859b2STodor Tomov #include <linux/mutex.h> 24ec6859b2STodor Tomov #include <linux/platform_device.h> 25ec6859b2STodor Tomov #include <media/media-entity.h> 26ec6859b2STodor Tomov #include <media/v4l2-device.h> 27ec6859b2STodor Tomov #include <media/v4l2-subdev.h> 28ec6859b2STodor Tomov 29ec6859b2STodor Tomov #include "camss-ispif.h" 30ec6859b2STodor Tomov #include "camss.h" 31ec6859b2STodor Tomov 32ec6859b2STodor Tomov #define MSM_ISPIF_NAME "msm_ispif" 33ec6859b2STodor Tomov 34ec6859b2STodor Tomov #define ispif_line_array(ptr_line) \ 35ec6859b2STodor Tomov ((const struct ispif_line (*)[]) &(ptr_line[-(ptr_line->id)])) 36ec6859b2STodor Tomov 37ec6859b2STodor Tomov #define to_ispif(ptr_line) \ 38ec6859b2STodor Tomov container_of(ispif_line_array(ptr_line), struct ispif_device, ptr_line) 39ec6859b2STodor Tomov 40ec6859b2STodor Tomov #define ISPIF_RST_CMD_0 0x008 41ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_STROBED_RST_EN (1 << 0) 42ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_MISC_LOGIC_RST (1 << 1) 43ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_SW_REG_RST (1 << 2) 44ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_PIX_INTF_0_CSID_RST (1 << 3) 45ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_PIX_INTF_0_VFE_RST (1 << 4) 46ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_PIX_INTF_1_CSID_RST (1 << 5) 47ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_PIX_INTF_1_VFE_RST (1 << 6) 48ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_RDI_INTF_0_CSID_RST (1 << 7) 49ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_RDI_INTF_0_VFE_RST (1 << 8) 50ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_RDI_INTF_1_CSID_RST (1 << 9) 51ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_RDI_INTF_1_VFE_RST (1 << 10) 52ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_RDI_INTF_2_CSID_RST (1 << 11) 53ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_RDI_INTF_2_VFE_RST (1 << 12) 54ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_PIX_OUTPUT_0_MISR_RST (1 << 16) 55ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_RDI_OUTPUT_0_MISR_RST (1 << 17) 56ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_RDI_OUTPUT_1_MISR_RST (1 << 18) 57ec6859b2STodor Tomov #define ISPIF_RST_CMD_0_RDI_OUTPUT_2_MISR_RST (1 << 19) 58ec6859b2STodor Tomov #define ISPIF_IRQ_GLOBAL_CLEAR_CMD 0x01c 59ec6859b2STodor Tomov #define ISPIF_VFE_m_CTRL_0(m) (0x200 + 0x200 * (m)) 60ec6859b2STodor Tomov #define ISPIF_VFE_m_CTRL_0_PIX0_LINE_BUF_EN (1 << 6) 61ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_0(m) (0x208 + 0x200 * (m)) 62ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE 0x00001249 63ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_0_PIX0_MASK 0x00001fff 64ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE 0x02492000 65ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_0_RDI0_MASK 0x03ffe000 66ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_1(m) (0x20c + 0x200 * (m)) 67ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE 0x00001249 68ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_1_PIX1_MASK 0x00001fff 69ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE 0x02492000 70ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_1_RDI1_MASK 0x03ffe000 71ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_2(m) (0x210 + 0x200 * (m)) 72ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE 0x00001249 73ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_MASK_2_RDI2_MASK 0x00001fff 74ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_STATUS_0(m) (0x21c + 0x200 * (m)) 75ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW (1 << 12) 76ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW (1 << 25) 77ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_STATUS_1(m) (0x220 + 0x200 * (m)) 78ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW (1 << 12) 79ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW (1 << 25) 80ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_STATUS_2(m) (0x224 + 0x200 * (m)) 81ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW (1 << 12) 82ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_CLEAR_0(m) (0x230 + 0x200 * (m)) 83ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_CLEAR_1(m) (0x234 + 0x200 * (m)) 84ec6859b2STodor Tomov #define ISPIF_VFE_m_IRQ_CLEAR_2(m) (0x238 + 0x200 * (m)) 85ec6859b2STodor Tomov #define ISPIF_VFE_m_INTF_INPUT_SEL(m) (0x244 + 0x200 * (m)) 86ec6859b2STodor Tomov #define ISPIF_VFE_m_INTF_CMD_0(m) (0x248 + 0x200 * (m)) 87ec6859b2STodor Tomov #define ISPIF_VFE_m_INTF_CMD_1(m) (0x24c + 0x200 * (m)) 88ec6859b2STodor Tomov #define ISPIF_VFE_m_PIX_INTF_n_CID_MASK(m, n) \ 89ec6859b2STodor Tomov (0x254 + 0x200 * (m) + 0x4 * (n)) 90ec6859b2STodor Tomov #define ISPIF_VFE_m_RDI_INTF_n_CID_MASK(m, n) \ 91ec6859b2STodor Tomov (0x264 + 0x200 * (m) + 0x4 * (n)) 92ec6859b2STodor Tomov #define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n) \ 93ec6859b2STodor Tomov (0x2c0 + 0x200 * (m) + 0x4 * (n)) 94ec6859b2STodor Tomov #define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n) \ 95ec6859b2STodor Tomov (0x2d0 + 0x200 * (m) + 0x4 * (n)) 96ec6859b2STodor Tomov 97ec6859b2STodor Tomov #define CSI_PIX_CLK_MUX_SEL 0x000 98ec6859b2STodor Tomov #define CSI_RDI_CLK_MUX_SEL 0x008 99ec6859b2STodor Tomov 100ec6859b2STodor Tomov #define ISPIF_TIMEOUT_SLEEP_US 1000 101ec6859b2STodor Tomov #define ISPIF_TIMEOUT_ALL_US 1000000 102ec6859b2STodor Tomov #define ISPIF_RESET_TIMEOUT_MS 500 103ec6859b2STodor Tomov 104ec6859b2STodor Tomov enum ispif_intf_cmd { 105ec6859b2STodor Tomov CMD_DISABLE_FRAME_BOUNDARY = 0x0, 106ec6859b2STodor Tomov CMD_ENABLE_FRAME_BOUNDARY = 0x1, 107ec6859b2STodor Tomov CMD_DISABLE_IMMEDIATELY = 0x2, 108ec6859b2STodor Tomov CMD_ALL_DISABLE_IMMEDIATELY = 0xaaaaaaaa, 109ec6859b2STodor Tomov CMD_ALL_NO_CHANGE = 0xffffffff, 110ec6859b2STodor Tomov }; 111ec6859b2STodor Tomov 112ec6859b2STodor Tomov static const u32 ispif_formats[] = { 113ec6859b2STodor Tomov MEDIA_BUS_FMT_UYVY8_2X8, 114ec6859b2STodor Tomov MEDIA_BUS_FMT_VYUY8_2X8, 115ec6859b2STodor Tomov MEDIA_BUS_FMT_YUYV8_2X8, 116ec6859b2STodor Tomov MEDIA_BUS_FMT_YVYU8_2X8, 117ec6859b2STodor Tomov MEDIA_BUS_FMT_SBGGR8_1X8, 118ec6859b2STodor Tomov MEDIA_BUS_FMT_SGBRG8_1X8, 119ec6859b2STodor Tomov MEDIA_BUS_FMT_SGRBG8_1X8, 120ec6859b2STodor Tomov MEDIA_BUS_FMT_SRGGB8_1X8, 121ec6859b2STodor Tomov MEDIA_BUS_FMT_SBGGR10_1X10, 122ec6859b2STodor Tomov MEDIA_BUS_FMT_SGBRG10_1X10, 123ec6859b2STodor Tomov MEDIA_BUS_FMT_SGRBG10_1X10, 124ec6859b2STodor Tomov MEDIA_BUS_FMT_SRGGB10_1X10, 125ec6859b2STodor Tomov MEDIA_BUS_FMT_SBGGR12_1X12, 126ec6859b2STodor Tomov MEDIA_BUS_FMT_SGBRG12_1X12, 127ec6859b2STodor Tomov MEDIA_BUS_FMT_SGRBG12_1X12, 128ec6859b2STodor Tomov MEDIA_BUS_FMT_SRGGB12_1X12, 129ec6859b2STodor Tomov }; 130ec6859b2STodor Tomov 131ec6859b2STodor Tomov /* 132ec6859b2STodor Tomov * ispif_isr - ISPIF module interrupt handler 133ec6859b2STodor Tomov * @irq: Interrupt line 134ec6859b2STodor Tomov * @dev: ISPIF device 135ec6859b2STodor Tomov * 136ec6859b2STodor Tomov * Return IRQ_HANDLED on success 137ec6859b2STodor Tomov */ 138ec6859b2STodor Tomov static irqreturn_t ispif_isr(int irq, void *dev) 139ec6859b2STodor Tomov { 140ec6859b2STodor Tomov struct ispif_device *ispif = dev; 141ec6859b2STodor Tomov u32 value0, value1, value2; 142ec6859b2STodor Tomov 143ec6859b2STodor Tomov value0 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(0)); 144ec6859b2STodor Tomov value1 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_1(0)); 145ec6859b2STodor Tomov value2 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_2(0)); 146ec6859b2STodor Tomov 147ec6859b2STodor Tomov writel_relaxed(value0, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(0)); 148ec6859b2STodor Tomov writel_relaxed(value1, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(0)); 149ec6859b2STodor Tomov writel_relaxed(value2, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(0)); 150ec6859b2STodor Tomov 151ec6859b2STodor Tomov writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD); 152ec6859b2STodor Tomov 153ec6859b2STodor Tomov if ((value0 >> 27) & 0x1) 154ec6859b2STodor Tomov complete(&ispif->reset_complete); 155ec6859b2STodor Tomov 156ec6859b2STodor Tomov if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW)) 157ec6859b2STodor Tomov dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n"); 158ec6859b2STodor Tomov 159ec6859b2STodor Tomov if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW)) 160ec6859b2STodor Tomov dev_err_ratelimited(to_device(ispif), "VFE0 rdi0 overflow\n"); 161ec6859b2STodor Tomov 162ec6859b2STodor Tomov if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW)) 163ec6859b2STodor Tomov dev_err_ratelimited(to_device(ispif), "VFE0 pix1 overflow\n"); 164ec6859b2STodor Tomov 165ec6859b2STodor Tomov if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW)) 166ec6859b2STodor Tomov dev_err_ratelimited(to_device(ispif), "VFE0 rdi1 overflow\n"); 167ec6859b2STodor Tomov 168ec6859b2STodor Tomov if (unlikely(value2 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW)) 169ec6859b2STodor Tomov dev_err_ratelimited(to_device(ispif), "VFE0 rdi2 overflow\n"); 170ec6859b2STodor Tomov 171ec6859b2STodor Tomov return IRQ_HANDLED; 172ec6859b2STodor Tomov } 173ec6859b2STodor Tomov 174ec6859b2STodor Tomov /* 175ec6859b2STodor Tomov * ispif_reset - Trigger reset on ISPIF module and wait to complete 176ec6859b2STodor Tomov * @ispif: ISPIF device 177ec6859b2STodor Tomov * 178ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 179ec6859b2STodor Tomov */ 180ec6859b2STodor Tomov static int ispif_reset(struct ispif_device *ispif) 181ec6859b2STodor Tomov { 182ec6859b2STodor Tomov unsigned long time; 183ec6859b2STodor Tomov u32 val; 184ec6859b2STodor Tomov int ret; 185ec6859b2STodor Tomov 186ec6859b2STodor Tomov ret = camss_enable_clocks(ispif->nclocks_for_reset, 187ec6859b2STodor Tomov ispif->clock_for_reset, 188ec6859b2STodor Tomov to_device(ispif)); 189ec6859b2STodor Tomov if (ret < 0) 190ec6859b2STodor Tomov return ret; 191ec6859b2STodor Tomov 192ec6859b2STodor Tomov reinit_completion(&ispif->reset_complete); 193ec6859b2STodor Tomov 194ec6859b2STodor Tomov val = ISPIF_RST_CMD_0_STROBED_RST_EN | 195ec6859b2STodor Tomov ISPIF_RST_CMD_0_MISC_LOGIC_RST | 196ec6859b2STodor Tomov ISPIF_RST_CMD_0_SW_REG_RST | 197ec6859b2STodor Tomov ISPIF_RST_CMD_0_PIX_INTF_0_CSID_RST | 198ec6859b2STodor Tomov ISPIF_RST_CMD_0_PIX_INTF_0_VFE_RST | 199ec6859b2STodor Tomov ISPIF_RST_CMD_0_PIX_INTF_1_CSID_RST | 200ec6859b2STodor Tomov ISPIF_RST_CMD_0_PIX_INTF_1_VFE_RST | 201ec6859b2STodor Tomov ISPIF_RST_CMD_0_RDI_INTF_0_CSID_RST | 202ec6859b2STodor Tomov ISPIF_RST_CMD_0_RDI_INTF_0_VFE_RST | 203ec6859b2STodor Tomov ISPIF_RST_CMD_0_RDI_INTF_1_CSID_RST | 204ec6859b2STodor Tomov ISPIF_RST_CMD_0_RDI_INTF_1_VFE_RST | 205ec6859b2STodor Tomov ISPIF_RST_CMD_0_RDI_INTF_2_CSID_RST | 206ec6859b2STodor Tomov ISPIF_RST_CMD_0_RDI_INTF_2_VFE_RST | 207ec6859b2STodor Tomov ISPIF_RST_CMD_0_PIX_OUTPUT_0_MISR_RST | 208ec6859b2STodor Tomov ISPIF_RST_CMD_0_RDI_OUTPUT_0_MISR_RST | 209ec6859b2STodor Tomov ISPIF_RST_CMD_0_RDI_OUTPUT_1_MISR_RST | 210ec6859b2STodor Tomov ISPIF_RST_CMD_0_RDI_OUTPUT_2_MISR_RST; 211ec6859b2STodor Tomov 212ec6859b2STodor Tomov writel_relaxed(val, ispif->base + ISPIF_RST_CMD_0); 213ec6859b2STodor Tomov 214ec6859b2STodor Tomov time = wait_for_completion_timeout(&ispif->reset_complete, 215ec6859b2STodor Tomov msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS)); 216ec6859b2STodor Tomov if (!time) { 217ec6859b2STodor Tomov dev_err(to_device(ispif), "ISPIF reset timeout\n"); 218ec6859b2STodor Tomov return -EIO; 219ec6859b2STodor Tomov } 220ec6859b2STodor Tomov 221ec6859b2STodor Tomov camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset); 222ec6859b2STodor Tomov 223ec6859b2STodor Tomov return 0; 224ec6859b2STodor Tomov } 225ec6859b2STodor Tomov 226ec6859b2STodor Tomov /* 227ec6859b2STodor Tomov * ispif_set_power - Power on/off ISPIF module 228ec6859b2STodor Tomov * @sd: ISPIF V4L2 subdevice 229ec6859b2STodor Tomov * @on: Requested power state 230ec6859b2STodor Tomov * 231ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 232ec6859b2STodor Tomov */ 233ec6859b2STodor Tomov static int ispif_set_power(struct v4l2_subdev *sd, int on) 234ec6859b2STodor Tomov { 235ec6859b2STodor Tomov struct ispif_line *line = v4l2_get_subdevdata(sd); 236ec6859b2STodor Tomov struct ispif_device *ispif = to_ispif(line); 237ec6859b2STodor Tomov struct device *dev = to_device(ispif); 238ec6859b2STodor Tomov int ret = 0; 239ec6859b2STodor Tomov 240ec6859b2STodor Tomov mutex_lock(&ispif->power_lock); 241ec6859b2STodor Tomov 242ec6859b2STodor Tomov if (on) { 243ec6859b2STodor Tomov if (ispif->power_count) { 244ec6859b2STodor Tomov /* Power is already on */ 245ec6859b2STodor Tomov ispif->power_count++; 246ec6859b2STodor Tomov goto exit; 247ec6859b2STodor Tomov } 248ec6859b2STodor Tomov 249ec6859b2STodor Tomov ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); 250ec6859b2STodor Tomov if (ret < 0) 251ec6859b2STodor Tomov goto exit; 252ec6859b2STodor Tomov 253ec6859b2STodor Tomov ret = ispif_reset(ispif); 254ec6859b2STodor Tomov if (ret < 0) { 255ec6859b2STodor Tomov camss_disable_clocks(ispif->nclocks, ispif->clock); 256ec6859b2STodor Tomov goto exit; 257ec6859b2STodor Tomov } 258ec6859b2STodor Tomov 259ec6859b2STodor Tomov ispif->intf_cmd[line->vfe_id].cmd_0 = CMD_ALL_NO_CHANGE; 260ec6859b2STodor Tomov ispif->intf_cmd[line->vfe_id].cmd_1 = CMD_ALL_NO_CHANGE; 261ec6859b2STodor Tomov 262ec6859b2STodor Tomov ispif->power_count++; 263ec6859b2STodor Tomov } else { 264ec6859b2STodor Tomov if (ispif->power_count == 0) { 265ec6859b2STodor Tomov dev_err(dev, "ispif power off on power_count == 0\n"); 266ec6859b2STodor Tomov goto exit; 267ec6859b2STodor Tomov } else if (ispif->power_count == 1) { 268ec6859b2STodor Tomov camss_disable_clocks(ispif->nclocks, ispif->clock); 269ec6859b2STodor Tomov } 270ec6859b2STodor Tomov 271ec6859b2STodor Tomov ispif->power_count--; 272ec6859b2STodor Tomov } 273ec6859b2STodor Tomov 274ec6859b2STodor Tomov exit: 275ec6859b2STodor Tomov mutex_unlock(&ispif->power_lock); 276ec6859b2STodor Tomov 277ec6859b2STodor Tomov return ret; 278ec6859b2STodor Tomov } 279ec6859b2STodor Tomov 280ec6859b2STodor Tomov /* 281ec6859b2STodor Tomov * ispif_select_clk_mux - Select clock for PIX/RDI interface 282ec6859b2STodor Tomov * @ispif: ISPIF device 283ec6859b2STodor Tomov * @intf: VFE interface 284ec6859b2STodor Tomov * @csid: CSID HW module id 285ec6859b2STodor Tomov * @vfe: VFE HW module id 286ec6859b2STodor Tomov * @enable: enable or disable the selected clock 287ec6859b2STodor Tomov */ 288ec6859b2STodor Tomov static void ispif_select_clk_mux(struct ispif_device *ispif, 289ec6859b2STodor Tomov enum ispif_intf intf, u8 csid, 290ec6859b2STodor Tomov u8 vfe, u8 enable) 291ec6859b2STodor Tomov { 292ec6859b2STodor Tomov u32 val; 293ec6859b2STodor Tomov 294ec6859b2STodor Tomov switch (intf) { 295ec6859b2STodor Tomov case PIX0: 296ec6859b2STodor Tomov val = readl_relaxed(ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL); 297ec6859b2STodor Tomov val &= ~(0xf << (vfe * 8)); 298ec6859b2STodor Tomov if (enable) 299ec6859b2STodor Tomov val |= (csid << (vfe * 8)); 300ec6859b2STodor Tomov writel_relaxed(val, ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL); 301ec6859b2STodor Tomov break; 302ec6859b2STodor Tomov 303ec6859b2STodor Tomov case RDI0: 304ec6859b2STodor Tomov val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); 305ec6859b2STodor Tomov val &= ~(0xf << (vfe * 12)); 306ec6859b2STodor Tomov if (enable) 307ec6859b2STodor Tomov val |= (csid << (vfe * 12)); 308ec6859b2STodor Tomov writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); 309ec6859b2STodor Tomov break; 310ec6859b2STodor Tomov 311ec6859b2STodor Tomov case PIX1: 312ec6859b2STodor Tomov val = readl_relaxed(ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL); 313ec6859b2STodor Tomov val &= ~(0xf << (4 + (vfe * 8))); 314ec6859b2STodor Tomov if (enable) 315ec6859b2STodor Tomov val |= (csid << (4 + (vfe * 8))); 316ec6859b2STodor Tomov writel_relaxed(val, ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL); 317ec6859b2STodor Tomov break; 318ec6859b2STodor Tomov 319ec6859b2STodor Tomov case RDI1: 320ec6859b2STodor Tomov val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); 321ec6859b2STodor Tomov val &= ~(0xf << (4 + (vfe * 12))); 322ec6859b2STodor Tomov if (enable) 323ec6859b2STodor Tomov val |= (csid << (4 + (vfe * 12))); 324ec6859b2STodor Tomov writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); 325ec6859b2STodor Tomov break; 326ec6859b2STodor Tomov 327ec6859b2STodor Tomov case RDI2: 328ec6859b2STodor Tomov val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); 329ec6859b2STodor Tomov val &= ~(0xf << (8 + (vfe * 12))); 330ec6859b2STodor Tomov if (enable) 331ec6859b2STodor Tomov val |= (csid << (8 + (vfe * 12))); 332ec6859b2STodor Tomov writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL); 333ec6859b2STodor Tomov break; 334ec6859b2STodor Tomov } 335ec6859b2STodor Tomov 336ec6859b2STodor Tomov mb(); 337ec6859b2STodor Tomov } 338ec6859b2STodor Tomov 339ec6859b2STodor Tomov /* 340ec6859b2STodor Tomov * ispif_validate_intf_status - Validate current status of PIX/RDI interface 341ec6859b2STodor Tomov * @ispif: ISPIF device 342ec6859b2STodor Tomov * @intf: VFE interface 343ec6859b2STodor Tomov * @vfe: VFE HW module id 344ec6859b2STodor Tomov * 345ec6859b2STodor Tomov * Return 0 when interface is idle or -EBUSY otherwise 346ec6859b2STodor Tomov */ 347ec6859b2STodor Tomov static int ispif_validate_intf_status(struct ispif_device *ispif, 348ec6859b2STodor Tomov enum ispif_intf intf, u8 vfe) 349ec6859b2STodor Tomov { 350ec6859b2STodor Tomov int ret = 0; 351ec6859b2STodor Tomov u32 val = 0; 352ec6859b2STodor Tomov 353ec6859b2STodor Tomov switch (intf) { 354ec6859b2STodor Tomov case PIX0: 355ec6859b2STodor Tomov val = readl_relaxed(ispif->base + 356ec6859b2STodor Tomov ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 0)); 357ec6859b2STodor Tomov break; 358ec6859b2STodor Tomov case RDI0: 359ec6859b2STodor Tomov val = readl_relaxed(ispif->base + 360ec6859b2STodor Tomov ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 0)); 361ec6859b2STodor Tomov break; 362ec6859b2STodor Tomov case PIX1: 363ec6859b2STodor Tomov val = readl_relaxed(ispif->base + 364ec6859b2STodor Tomov ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 1)); 365ec6859b2STodor Tomov break; 366ec6859b2STodor Tomov case RDI1: 367ec6859b2STodor Tomov val = readl_relaxed(ispif->base + 368ec6859b2STodor Tomov ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 1)); 369ec6859b2STodor Tomov break; 370ec6859b2STodor Tomov case RDI2: 371ec6859b2STodor Tomov val = readl_relaxed(ispif->base + 372ec6859b2STodor Tomov ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 2)); 373ec6859b2STodor Tomov break; 374ec6859b2STodor Tomov } 375ec6859b2STodor Tomov 376ec6859b2STodor Tomov if ((val & 0xf) != 0xf) { 377ec6859b2STodor Tomov dev_err(to_device(ispif), "%s: ispif is busy: 0x%x\n", 378ec6859b2STodor Tomov __func__, val); 379ec6859b2STodor Tomov ret = -EBUSY; 380ec6859b2STodor Tomov } 381ec6859b2STodor Tomov 382ec6859b2STodor Tomov return ret; 383ec6859b2STodor Tomov } 384ec6859b2STodor Tomov 385ec6859b2STodor Tomov /* 386ec6859b2STodor Tomov * ispif_wait_for_stop - Wait for PIX/RDI interface to stop 387ec6859b2STodor Tomov * @ispif: ISPIF device 388ec6859b2STodor Tomov * @intf: VFE interface 389ec6859b2STodor Tomov * @vfe: VFE HW module id 390ec6859b2STodor Tomov * 391ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 392ec6859b2STodor Tomov */ 393ec6859b2STodor Tomov static int ispif_wait_for_stop(struct ispif_device *ispif, 394ec6859b2STodor Tomov enum ispif_intf intf, u8 vfe) 395ec6859b2STodor Tomov { 396ec6859b2STodor Tomov u32 addr = 0; 397ec6859b2STodor Tomov u32 stop_flag = 0; 398ec6859b2STodor Tomov int ret; 399ec6859b2STodor Tomov 400ec6859b2STodor Tomov switch (intf) { 401ec6859b2STodor Tomov case PIX0: 402ec6859b2STodor Tomov addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 0); 403ec6859b2STodor Tomov break; 404ec6859b2STodor Tomov case RDI0: 405ec6859b2STodor Tomov addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 0); 406ec6859b2STodor Tomov break; 407ec6859b2STodor Tomov case PIX1: 408ec6859b2STodor Tomov addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 1); 409ec6859b2STodor Tomov break; 410ec6859b2STodor Tomov case RDI1: 411ec6859b2STodor Tomov addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 1); 412ec6859b2STodor Tomov break; 413ec6859b2STodor Tomov case RDI2: 414ec6859b2STodor Tomov addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 2); 415ec6859b2STodor Tomov break; 416ec6859b2STodor Tomov } 417ec6859b2STodor Tomov 418ec6859b2STodor Tomov ret = readl_poll_timeout(ispif->base + addr, 419ec6859b2STodor Tomov stop_flag, 420ec6859b2STodor Tomov (stop_flag & 0xf) == 0xf, 421ec6859b2STodor Tomov ISPIF_TIMEOUT_SLEEP_US, 422ec6859b2STodor Tomov ISPIF_TIMEOUT_ALL_US); 423ec6859b2STodor Tomov if (ret < 0) 424ec6859b2STodor Tomov dev_err(to_device(ispif), "%s: ispif stop timeout\n", 425ec6859b2STodor Tomov __func__); 426ec6859b2STodor Tomov 427ec6859b2STodor Tomov return ret; 428ec6859b2STodor Tomov } 429ec6859b2STodor Tomov 430ec6859b2STodor Tomov /* 431ec6859b2STodor Tomov * ispif_select_csid - Select CSID HW module for input from 432ec6859b2STodor Tomov * @ispif: ISPIF device 433ec6859b2STodor Tomov * @intf: VFE interface 434ec6859b2STodor Tomov * @csid: CSID HW module id 435ec6859b2STodor Tomov * @vfe: VFE HW module id 436ec6859b2STodor Tomov * @enable: enable or disable the selected input 437ec6859b2STodor Tomov */ 438ec6859b2STodor Tomov static void ispif_select_csid(struct ispif_device *ispif, enum ispif_intf intf, 439ec6859b2STodor Tomov u8 csid, u8 vfe, u8 enable) 440ec6859b2STodor Tomov { 441ec6859b2STodor Tomov u32 val; 442ec6859b2STodor Tomov 443ec6859b2STodor Tomov val = readl_relaxed(ispif->base + ISPIF_VFE_m_INTF_INPUT_SEL(vfe)); 444ec6859b2STodor Tomov switch (intf) { 445ec6859b2STodor Tomov case PIX0: 446ec6859b2STodor Tomov val &= ~(BIT(1) | BIT(0)); 447ec6859b2STodor Tomov if (enable) 448ec6859b2STodor Tomov val |= csid; 449ec6859b2STodor Tomov break; 450ec6859b2STodor Tomov case RDI0: 451ec6859b2STodor Tomov val &= ~(BIT(5) | BIT(4)); 452ec6859b2STodor Tomov if (enable) 453ec6859b2STodor Tomov val |= (csid << 4); 454ec6859b2STodor Tomov break; 455ec6859b2STodor Tomov case PIX1: 456ec6859b2STodor Tomov val &= ~(BIT(9) | BIT(8)); 457ec6859b2STodor Tomov if (enable) 458ec6859b2STodor Tomov val |= (csid << 8); 459ec6859b2STodor Tomov break; 460ec6859b2STodor Tomov case RDI1: 461ec6859b2STodor Tomov val &= ~(BIT(13) | BIT(12)); 462ec6859b2STodor Tomov if (enable) 463ec6859b2STodor Tomov val |= (csid << 12); 464ec6859b2STodor Tomov break; 465ec6859b2STodor Tomov case RDI2: 466ec6859b2STodor Tomov val &= ~(BIT(21) | BIT(20)); 467ec6859b2STodor Tomov if (enable) 468ec6859b2STodor Tomov val |= (csid << 20); 469ec6859b2STodor Tomov break; 470ec6859b2STodor Tomov } 471ec6859b2STodor Tomov 472ec6859b2STodor Tomov writel(val, ispif->base + ISPIF_VFE_m_INTF_INPUT_SEL(vfe)); 473ec6859b2STodor Tomov } 474ec6859b2STodor Tomov 475ec6859b2STodor Tomov /* 476ec6859b2STodor Tomov * ispif_select_cid - Enable/disable desired CID 477ec6859b2STodor Tomov * @ispif: ISPIF device 478ec6859b2STodor Tomov * @intf: VFE interface 479ec6859b2STodor Tomov * @cid: desired CID to enable/disable 480ec6859b2STodor Tomov * @vfe: VFE HW module id 481ec6859b2STodor Tomov * @enable: enable or disable the desired CID 482ec6859b2STodor Tomov */ 483ec6859b2STodor Tomov static void ispif_select_cid(struct ispif_device *ispif, enum ispif_intf intf, 484ec6859b2STodor Tomov u8 cid, u8 vfe, u8 enable) 485ec6859b2STodor Tomov { 486ec6859b2STodor Tomov u32 cid_mask = 1 << cid; 487ec6859b2STodor Tomov u32 addr = 0; 488ec6859b2STodor Tomov u32 val; 489ec6859b2STodor Tomov 490ec6859b2STodor Tomov switch (intf) { 491ec6859b2STodor Tomov case PIX0: 492ec6859b2STodor Tomov addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe, 0); 493ec6859b2STodor Tomov break; 494ec6859b2STodor Tomov case RDI0: 495ec6859b2STodor Tomov addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 0); 496ec6859b2STodor Tomov break; 497ec6859b2STodor Tomov case PIX1: 498ec6859b2STodor Tomov addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe, 1); 499ec6859b2STodor Tomov break; 500ec6859b2STodor Tomov case RDI1: 501ec6859b2STodor Tomov addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 1); 502ec6859b2STodor Tomov break; 503ec6859b2STodor Tomov case RDI2: 504ec6859b2STodor Tomov addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 2); 505ec6859b2STodor Tomov break; 506ec6859b2STodor Tomov } 507ec6859b2STodor Tomov 508ec6859b2STodor Tomov val = readl_relaxed(ispif->base + addr); 509ec6859b2STodor Tomov if (enable) 510ec6859b2STodor Tomov val |= cid_mask; 511ec6859b2STodor Tomov else 512ec6859b2STodor Tomov val &= ~cid_mask; 513ec6859b2STodor Tomov 514ec6859b2STodor Tomov writel(val, ispif->base + addr); 515ec6859b2STodor Tomov } 516ec6859b2STodor Tomov 517ec6859b2STodor Tomov /* 518ec6859b2STodor Tomov * ispif_config_irq - Enable/disable interrupts for PIX/RDI interface 519ec6859b2STodor Tomov * @ispif: ISPIF device 520ec6859b2STodor Tomov * @intf: VFE interface 521ec6859b2STodor Tomov * @vfe: VFE HW module id 522ec6859b2STodor Tomov * @enable: enable or disable 523ec6859b2STodor Tomov */ 524ec6859b2STodor Tomov static void ispif_config_irq(struct ispif_device *ispif, enum ispif_intf intf, 525ec6859b2STodor Tomov u8 vfe, u8 enable) 526ec6859b2STodor Tomov { 527ec6859b2STodor Tomov u32 val; 528ec6859b2STodor Tomov 529ec6859b2STodor Tomov switch (intf) { 530ec6859b2STodor Tomov case PIX0: 531ec6859b2STodor Tomov val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe)); 532ec6859b2STodor Tomov val &= ~ISPIF_VFE_m_IRQ_MASK_0_PIX0_MASK; 533ec6859b2STodor Tomov if (enable) 534ec6859b2STodor Tomov val |= ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE; 535ec6859b2STodor Tomov writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe)); 536ec6859b2STodor Tomov writel_relaxed(ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE, 537ec6859b2STodor Tomov ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(vfe)); 538ec6859b2STodor Tomov break; 539ec6859b2STodor Tomov case RDI0: 540ec6859b2STodor Tomov val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe)); 541ec6859b2STodor Tomov val &= ~ISPIF_VFE_m_IRQ_MASK_0_RDI0_MASK; 542ec6859b2STodor Tomov if (enable) 543ec6859b2STodor Tomov val |= ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE; 544ec6859b2STodor Tomov writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe)); 545ec6859b2STodor Tomov writel_relaxed(ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE, 546ec6859b2STodor Tomov ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(vfe)); 547ec6859b2STodor Tomov break; 548ec6859b2STodor Tomov case PIX1: 549ec6859b2STodor Tomov val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe)); 550ec6859b2STodor Tomov val &= ~ISPIF_VFE_m_IRQ_MASK_1_PIX1_MASK; 551ec6859b2STodor Tomov if (enable) 552ec6859b2STodor Tomov val |= ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE; 553ec6859b2STodor Tomov writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe)); 554ec6859b2STodor Tomov writel_relaxed(ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE, 555ec6859b2STodor Tomov ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(vfe)); 556ec6859b2STodor Tomov break; 557ec6859b2STodor Tomov case RDI1: 558ec6859b2STodor Tomov val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe)); 559ec6859b2STodor Tomov val &= ~ISPIF_VFE_m_IRQ_MASK_1_RDI1_MASK; 560ec6859b2STodor Tomov if (enable) 561ec6859b2STodor Tomov val |= ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE; 562ec6859b2STodor Tomov writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe)); 563ec6859b2STodor Tomov writel_relaxed(ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE, 564ec6859b2STodor Tomov ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(vfe)); 565ec6859b2STodor Tomov break; 566ec6859b2STodor Tomov case RDI2: 567ec6859b2STodor Tomov val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_2(vfe)); 568ec6859b2STodor Tomov val &= ~ISPIF_VFE_m_IRQ_MASK_2_RDI2_MASK; 569ec6859b2STodor Tomov if (enable) 570ec6859b2STodor Tomov val |= ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE; 571ec6859b2STodor Tomov writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_2(vfe)); 572ec6859b2STodor Tomov writel_relaxed(ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE, 573ec6859b2STodor Tomov ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(vfe)); 574ec6859b2STodor Tomov break; 575ec6859b2STodor Tomov } 576ec6859b2STodor Tomov 577ec6859b2STodor Tomov writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD); 578ec6859b2STodor Tomov } 579ec6859b2STodor Tomov 580ec6859b2STodor Tomov /* 581ec6859b2STodor Tomov * ispif_set_intf_cmd - Set command to enable/disable interface 582ec6859b2STodor Tomov * @ispif: ISPIF device 583ec6859b2STodor Tomov * @cmd: interface command 584ec6859b2STodor Tomov * @intf: VFE interface 585ec6859b2STodor Tomov * @vfe: VFE HW module id 586ec6859b2STodor Tomov * @vc: virtual channel 587ec6859b2STodor Tomov */ 588ec6859b2STodor Tomov static void ispif_set_intf_cmd(struct ispif_device *ispif, u8 cmd, 589ec6859b2STodor Tomov enum ispif_intf intf, u8 vfe, u8 vc) 590ec6859b2STodor Tomov { 591ec6859b2STodor Tomov u32 *val; 592ec6859b2STodor Tomov 593ec6859b2STodor Tomov if (intf == RDI2) { 594ec6859b2STodor Tomov val = &ispif->intf_cmd[vfe].cmd_1; 595ec6859b2STodor Tomov *val &= ~(0x3 << (vc * 2 + 8)); 596ec6859b2STodor Tomov *val |= (cmd << (vc * 2 + 8)); 597ec6859b2STodor Tomov wmb(); 598ec6859b2STodor Tomov writel_relaxed(*val, ispif->base + ISPIF_VFE_m_INTF_CMD_1(vfe)); 599ec6859b2STodor Tomov wmb(); 600ec6859b2STodor Tomov } else { 601ec6859b2STodor Tomov val = &ispif->intf_cmd[vfe].cmd_0; 602ec6859b2STodor Tomov *val &= ~(0x3 << (vc * 2 + intf * 8)); 603ec6859b2STodor Tomov *val |= (cmd << (vc * 2 + intf * 8)); 604ec6859b2STodor Tomov wmb(); 605ec6859b2STodor Tomov writel_relaxed(*val, ispif->base + ISPIF_VFE_m_INTF_CMD_0(vfe)); 606ec6859b2STodor Tomov wmb(); 607ec6859b2STodor Tomov } 608ec6859b2STodor Tomov } 609ec6859b2STodor Tomov 610ec6859b2STodor Tomov /* 611ec6859b2STodor Tomov * ispif_set_stream - Enable/disable streaming on ISPIF module 612ec6859b2STodor Tomov * @sd: ISPIF V4L2 subdevice 613ec6859b2STodor Tomov * @enable: Requested streaming state 614ec6859b2STodor Tomov * 615ec6859b2STodor Tomov * Main configuration of ISPIF module is also done here. 616ec6859b2STodor Tomov * 617ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 618ec6859b2STodor Tomov */ 619ec6859b2STodor Tomov static int ispif_set_stream(struct v4l2_subdev *sd, int enable) 620ec6859b2STodor Tomov { 621ec6859b2STodor Tomov struct ispif_line *line = v4l2_get_subdevdata(sd); 622ec6859b2STodor Tomov struct ispif_device *ispif = to_ispif(line); 623ec6859b2STodor Tomov enum ispif_intf intf = line->interface; 624ec6859b2STodor Tomov u8 csid = line->csid_id; 625ec6859b2STodor Tomov u8 vfe = line->vfe_id; 626ec6859b2STodor Tomov u8 vc = 0; /* Virtual Channel 0 */ 627ec6859b2STodor Tomov u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ 628ec6859b2STodor Tomov int ret; 629ec6859b2STodor Tomov 630ec6859b2STodor Tomov if (enable) { 631ec6859b2STodor Tomov if (!media_entity_remote_pad(&line->pads[MSM_ISPIF_PAD_SINK])) 632ec6859b2STodor Tomov return -ENOLINK; 633ec6859b2STodor Tomov 634ec6859b2STodor Tomov /* Config */ 635ec6859b2STodor Tomov 636ec6859b2STodor Tomov mutex_lock(&ispif->config_lock); 637ec6859b2STodor Tomov ispif_select_clk_mux(ispif, intf, csid, vfe, 1); 638ec6859b2STodor Tomov 639ec6859b2STodor Tomov ret = ispif_validate_intf_status(ispif, intf, vfe); 640ec6859b2STodor Tomov if (ret < 0) { 641ec6859b2STodor Tomov mutex_unlock(&ispif->config_lock); 642ec6859b2STodor Tomov return ret; 643ec6859b2STodor Tomov } 644ec6859b2STodor Tomov 645ec6859b2STodor Tomov ispif_select_csid(ispif, intf, csid, vfe, 1); 646ec6859b2STodor Tomov ispif_select_cid(ispif, intf, cid, vfe, 1); 647ec6859b2STodor Tomov ispif_config_irq(ispif, intf, vfe, 1); 648ec6859b2STodor Tomov ispif_set_intf_cmd(ispif, CMD_ENABLE_FRAME_BOUNDARY, 649ec6859b2STodor Tomov intf, vfe, vc); 650ec6859b2STodor Tomov } else { 651ec6859b2STodor Tomov mutex_lock(&ispif->config_lock); 652ec6859b2STodor Tomov ispif_set_intf_cmd(ispif, CMD_DISABLE_FRAME_BOUNDARY, 653ec6859b2STodor Tomov intf, vfe, vc); 654ec6859b2STodor Tomov mutex_unlock(&ispif->config_lock); 655ec6859b2STodor Tomov 656ec6859b2STodor Tomov ret = ispif_wait_for_stop(ispif, intf, vfe); 657ec6859b2STodor Tomov if (ret < 0) 658ec6859b2STodor Tomov return ret; 659ec6859b2STodor Tomov 660ec6859b2STodor Tomov mutex_lock(&ispif->config_lock); 661ec6859b2STodor Tomov ispif_config_irq(ispif, intf, vfe, 0); 662ec6859b2STodor Tomov ispif_select_cid(ispif, intf, cid, vfe, 0); 663ec6859b2STodor Tomov ispif_select_csid(ispif, intf, csid, vfe, 0); 664ec6859b2STodor Tomov ispif_select_clk_mux(ispif, intf, csid, vfe, 0); 665ec6859b2STodor Tomov } 666ec6859b2STodor Tomov 667ec6859b2STodor Tomov mutex_unlock(&ispif->config_lock); 668ec6859b2STodor Tomov 669ec6859b2STodor Tomov return 0; 670ec6859b2STodor Tomov } 671ec6859b2STodor Tomov 672ec6859b2STodor Tomov /* 673ec6859b2STodor Tomov * __ispif_get_format - Get pointer to format structure 674ec6859b2STodor Tomov * @ispif: ISPIF line 675ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 676ec6859b2STodor Tomov * @pad: pad from which format is requested 677ec6859b2STodor Tomov * @which: TRY or ACTIVE format 678ec6859b2STodor Tomov * 679ec6859b2STodor Tomov * Return pointer to TRY or ACTIVE format structure 680ec6859b2STodor Tomov */ 681ec6859b2STodor Tomov static struct v4l2_mbus_framefmt * 682ec6859b2STodor Tomov __ispif_get_format(struct ispif_line *line, 683ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 684ec6859b2STodor Tomov unsigned int pad, 685ec6859b2STodor Tomov enum v4l2_subdev_format_whence which) 686ec6859b2STodor Tomov { 687ec6859b2STodor Tomov if (which == V4L2_SUBDEV_FORMAT_TRY) 688ec6859b2STodor Tomov return v4l2_subdev_get_try_format(&line->subdev, cfg, pad); 689ec6859b2STodor Tomov 690ec6859b2STodor Tomov return &line->fmt[pad]; 691ec6859b2STodor Tomov } 692ec6859b2STodor Tomov 693ec6859b2STodor Tomov /* 694ec6859b2STodor Tomov * ispif_try_format - Handle try format by pad subdev method 695ec6859b2STodor Tomov * @ispif: ISPIF line 696ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 697ec6859b2STodor Tomov * @pad: pad on which format is requested 698ec6859b2STodor Tomov * @fmt: pointer to v4l2 format structure 699ec6859b2STodor Tomov * @which: wanted subdev format 700ec6859b2STodor Tomov */ 701ec6859b2STodor Tomov static void ispif_try_format(struct ispif_line *line, 702ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 703ec6859b2STodor Tomov unsigned int pad, 704ec6859b2STodor Tomov struct v4l2_mbus_framefmt *fmt, 705ec6859b2STodor Tomov enum v4l2_subdev_format_whence which) 706ec6859b2STodor Tomov { 707ec6859b2STodor Tomov unsigned int i; 708ec6859b2STodor Tomov 709ec6859b2STodor Tomov switch (pad) { 710ec6859b2STodor Tomov case MSM_ISPIF_PAD_SINK: 711ec6859b2STodor Tomov /* Set format on sink pad */ 712ec6859b2STodor Tomov 713ec6859b2STodor Tomov for (i = 0; i < ARRAY_SIZE(ispif_formats); i++) 714ec6859b2STodor Tomov if (fmt->code == ispif_formats[i]) 715ec6859b2STodor Tomov break; 716ec6859b2STodor Tomov 717ec6859b2STodor Tomov /* If not found, use UYVY as default */ 718ec6859b2STodor Tomov if (i >= ARRAY_SIZE(ispif_formats)) 719ec6859b2STodor Tomov fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; 720ec6859b2STodor Tomov 721ec6859b2STodor Tomov fmt->width = clamp_t(u32, fmt->width, 1, 8191); 722ec6859b2STodor Tomov fmt->height = clamp_t(u32, fmt->height, 1, 8191); 723ec6859b2STodor Tomov 724ec6859b2STodor Tomov fmt->field = V4L2_FIELD_NONE; 725ec6859b2STodor Tomov fmt->colorspace = V4L2_COLORSPACE_SRGB; 726ec6859b2STodor Tomov 727ec6859b2STodor Tomov break; 728ec6859b2STodor Tomov 729ec6859b2STodor Tomov case MSM_ISPIF_PAD_SRC: 730ec6859b2STodor Tomov /* Set and return a format same as sink pad */ 731ec6859b2STodor Tomov 732ec6859b2STodor Tomov *fmt = *__ispif_get_format(line, cfg, MSM_ISPIF_PAD_SINK, 733ec6859b2STodor Tomov which); 734ec6859b2STodor Tomov 735ec6859b2STodor Tomov break; 736ec6859b2STodor Tomov } 737ec6859b2STodor Tomov 738ec6859b2STodor Tomov fmt->colorspace = V4L2_COLORSPACE_SRGB; 739ec6859b2STodor Tomov } 740ec6859b2STodor Tomov 741ec6859b2STodor Tomov /* 742ec6859b2STodor Tomov * ispif_enum_mbus_code - Handle pixel format enumeration 743ec6859b2STodor Tomov * @sd: ISPIF V4L2 subdevice 744ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 745ec6859b2STodor Tomov * @code: pointer to v4l2_subdev_mbus_code_enum structure 746ec6859b2STodor Tomov * return -EINVAL or zero on success 747ec6859b2STodor Tomov */ 748ec6859b2STodor Tomov static int ispif_enum_mbus_code(struct v4l2_subdev *sd, 749ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 750ec6859b2STodor Tomov struct v4l2_subdev_mbus_code_enum *code) 751ec6859b2STodor Tomov { 752ec6859b2STodor Tomov struct ispif_line *line = v4l2_get_subdevdata(sd); 753ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 754ec6859b2STodor Tomov 755ec6859b2STodor Tomov if (code->pad == MSM_ISPIF_PAD_SINK) { 756ec6859b2STodor Tomov if (code->index >= ARRAY_SIZE(ispif_formats)) 757ec6859b2STodor Tomov return -EINVAL; 758ec6859b2STodor Tomov 759ec6859b2STodor Tomov code->code = ispif_formats[code->index]; 760ec6859b2STodor Tomov } else { 761ec6859b2STodor Tomov if (code->index > 0) 762ec6859b2STodor Tomov return -EINVAL; 763ec6859b2STodor Tomov 764ec6859b2STodor Tomov format = __ispif_get_format(line, cfg, MSM_ISPIF_PAD_SINK, 765ec6859b2STodor Tomov code->which); 766ec6859b2STodor Tomov 767ec6859b2STodor Tomov code->code = format->code; 768ec6859b2STodor Tomov } 769ec6859b2STodor Tomov 770ec6859b2STodor Tomov return 0; 771ec6859b2STodor Tomov } 772ec6859b2STodor Tomov 773ec6859b2STodor Tomov /* 774ec6859b2STodor Tomov * ispif_enum_frame_size - Handle frame size enumeration 775ec6859b2STodor Tomov * @sd: ISPIF V4L2 subdevice 776ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 777ec6859b2STodor Tomov * @fse: pointer to v4l2_subdev_frame_size_enum structure 778ec6859b2STodor Tomov * return -EINVAL or zero on success 779ec6859b2STodor Tomov */ 780ec6859b2STodor Tomov static int ispif_enum_frame_size(struct v4l2_subdev *sd, 781ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 782ec6859b2STodor Tomov struct v4l2_subdev_frame_size_enum *fse) 783ec6859b2STodor Tomov { 784ec6859b2STodor Tomov struct ispif_line *line = v4l2_get_subdevdata(sd); 785ec6859b2STodor Tomov struct v4l2_mbus_framefmt format; 786ec6859b2STodor Tomov 787ec6859b2STodor Tomov if (fse->index != 0) 788ec6859b2STodor Tomov return -EINVAL; 789ec6859b2STodor Tomov 790ec6859b2STodor Tomov format.code = fse->code; 791ec6859b2STodor Tomov format.width = 1; 792ec6859b2STodor Tomov format.height = 1; 793ec6859b2STodor Tomov ispif_try_format(line, cfg, fse->pad, &format, fse->which); 794ec6859b2STodor Tomov fse->min_width = format.width; 795ec6859b2STodor Tomov fse->min_height = format.height; 796ec6859b2STodor Tomov 797ec6859b2STodor Tomov if (format.code != fse->code) 798ec6859b2STodor Tomov return -EINVAL; 799ec6859b2STodor Tomov 800ec6859b2STodor Tomov format.code = fse->code; 801ec6859b2STodor Tomov format.width = -1; 802ec6859b2STodor Tomov format.height = -1; 803ec6859b2STodor Tomov ispif_try_format(line, cfg, fse->pad, &format, fse->which); 804ec6859b2STodor Tomov fse->max_width = format.width; 805ec6859b2STodor Tomov fse->max_height = format.height; 806ec6859b2STodor Tomov 807ec6859b2STodor Tomov return 0; 808ec6859b2STodor Tomov } 809ec6859b2STodor Tomov 810ec6859b2STodor Tomov /* 811ec6859b2STodor Tomov * ispif_get_format - Handle get format by pads subdev method 812ec6859b2STodor Tomov * @sd: ISPIF V4L2 subdevice 813ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 814ec6859b2STodor Tomov * @fmt: pointer to v4l2 subdev format structure 815ec6859b2STodor Tomov * 816ec6859b2STodor Tomov * Return -EINVAL or zero on success 817ec6859b2STodor Tomov */ 818ec6859b2STodor Tomov static int ispif_get_format(struct v4l2_subdev *sd, 819ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 820ec6859b2STodor Tomov struct v4l2_subdev_format *fmt) 821ec6859b2STodor Tomov { 822ec6859b2STodor Tomov struct ispif_line *line = v4l2_get_subdevdata(sd); 823ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 824ec6859b2STodor Tomov 825ec6859b2STodor Tomov format = __ispif_get_format(line, cfg, fmt->pad, fmt->which); 826ec6859b2STodor Tomov if (format == NULL) 827ec6859b2STodor Tomov return -EINVAL; 828ec6859b2STodor Tomov 829ec6859b2STodor Tomov fmt->format = *format; 830ec6859b2STodor Tomov 831ec6859b2STodor Tomov return 0; 832ec6859b2STodor Tomov } 833ec6859b2STodor Tomov 834ec6859b2STodor Tomov /* 835ec6859b2STodor Tomov * ispif_set_format - Handle set format by pads subdev method 836ec6859b2STodor Tomov * @sd: ISPIF V4L2 subdevice 837ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 838ec6859b2STodor Tomov * @fmt: pointer to v4l2 subdev format structure 839ec6859b2STodor Tomov * 840ec6859b2STodor Tomov * Return -EINVAL or zero on success 841ec6859b2STodor Tomov */ 842ec6859b2STodor Tomov static int ispif_set_format(struct v4l2_subdev *sd, 843ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 844ec6859b2STodor Tomov struct v4l2_subdev_format *fmt) 845ec6859b2STodor Tomov { 846ec6859b2STodor Tomov struct ispif_line *line = v4l2_get_subdevdata(sd); 847ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 848ec6859b2STodor Tomov 849ec6859b2STodor Tomov format = __ispif_get_format(line, cfg, fmt->pad, fmt->which); 850ec6859b2STodor Tomov if (format == NULL) 851ec6859b2STodor Tomov return -EINVAL; 852ec6859b2STodor Tomov 853ec6859b2STodor Tomov ispif_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which); 854ec6859b2STodor Tomov *format = fmt->format; 855ec6859b2STodor Tomov 856ec6859b2STodor Tomov /* Propagate the format from sink to source */ 857ec6859b2STodor Tomov if (fmt->pad == MSM_ISPIF_PAD_SINK) { 858ec6859b2STodor Tomov format = __ispif_get_format(line, cfg, MSM_ISPIF_PAD_SRC, 859ec6859b2STodor Tomov fmt->which); 860ec6859b2STodor Tomov 861ec6859b2STodor Tomov *format = fmt->format; 862ec6859b2STodor Tomov ispif_try_format(line, cfg, MSM_ISPIF_PAD_SRC, format, 863ec6859b2STodor Tomov fmt->which); 864ec6859b2STodor Tomov } 865ec6859b2STodor Tomov 866ec6859b2STodor Tomov return 0; 867ec6859b2STodor Tomov } 868ec6859b2STodor Tomov 869ec6859b2STodor Tomov /* 870ec6859b2STodor Tomov * ispif_init_formats - Initialize formats on all pads 871ec6859b2STodor Tomov * @sd: ISPIF V4L2 subdevice 872ec6859b2STodor Tomov * @fh: V4L2 subdev file handle 873ec6859b2STodor Tomov * 874ec6859b2STodor Tomov * Initialize all pad formats with default values. 875ec6859b2STodor Tomov * 876ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 877ec6859b2STodor Tomov */ 878ec6859b2STodor Tomov static int ispif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 879ec6859b2STodor Tomov { 880ec6859b2STodor Tomov struct v4l2_subdev_format format = { 881ec6859b2STodor Tomov .pad = MSM_ISPIF_PAD_SINK, 882ec6859b2STodor Tomov .which = fh ? V4L2_SUBDEV_FORMAT_TRY : 883ec6859b2STodor Tomov V4L2_SUBDEV_FORMAT_ACTIVE, 884ec6859b2STodor Tomov .format = { 885ec6859b2STodor Tomov .code = MEDIA_BUS_FMT_UYVY8_2X8, 886ec6859b2STodor Tomov .width = 1920, 887ec6859b2STodor Tomov .height = 1080 888ec6859b2STodor Tomov } 889ec6859b2STodor Tomov }; 890ec6859b2STodor Tomov 891ec6859b2STodor Tomov return ispif_set_format(sd, fh ? fh->pad : NULL, &format); 892ec6859b2STodor Tomov } 893ec6859b2STodor Tomov 894ec6859b2STodor Tomov /* 895ec6859b2STodor Tomov * msm_ispif_subdev_init - Initialize ISPIF device structure and resources 896ec6859b2STodor Tomov * @ispif: ISPIF device 897ec6859b2STodor Tomov * @res: ISPIF module resources table 898ec6859b2STodor Tomov * 899ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 900ec6859b2STodor Tomov */ 901ec6859b2STodor Tomov int msm_ispif_subdev_init(struct ispif_device *ispif, 902ec6859b2STodor Tomov const struct resources_ispif *res) 903ec6859b2STodor Tomov { 904ec6859b2STodor Tomov struct device *dev = to_device(ispif); 905ec6859b2STodor Tomov struct platform_device *pdev = to_platform_device(dev); 906ec6859b2STodor Tomov struct resource *r; 907ec6859b2STodor Tomov int i; 908ec6859b2STodor Tomov int ret; 909ec6859b2STodor Tomov 910ec6859b2STodor Tomov /* Memory */ 911ec6859b2STodor Tomov 912ec6859b2STodor Tomov r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); 913ec6859b2STodor Tomov ispif->base = devm_ioremap_resource(dev, r); 914ec6859b2STodor Tomov if (IS_ERR(ispif->base)) { 915ec6859b2STodor Tomov dev_err(dev, "could not map memory\n"); 916ec6859b2STodor Tomov return PTR_ERR(ispif->base); 917ec6859b2STodor Tomov } 918ec6859b2STodor Tomov 919ec6859b2STodor Tomov r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]); 920ec6859b2STodor Tomov ispif->base_clk_mux = devm_ioremap_resource(dev, r); 921ec6859b2STodor Tomov if (IS_ERR(ispif->base_clk_mux)) { 922ec6859b2STodor Tomov dev_err(dev, "could not map memory\n"); 923ec6859b2STodor Tomov return PTR_ERR(ispif->base_clk_mux); 924ec6859b2STodor Tomov } 925ec6859b2STodor Tomov 926ec6859b2STodor Tomov /* Interrupt */ 927ec6859b2STodor Tomov 928ec6859b2STodor Tomov r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res->interrupt); 929ec6859b2STodor Tomov 930ec6859b2STodor Tomov if (!r) { 931ec6859b2STodor Tomov dev_err(dev, "missing IRQ\n"); 932ec6859b2STodor Tomov return -EINVAL; 933ec6859b2STodor Tomov } 934ec6859b2STodor Tomov 935ec6859b2STodor Tomov ispif->irq = r->start; 936ec6859b2STodor Tomov snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s", 937ec6859b2STodor Tomov dev_name(dev), MSM_ISPIF_NAME); 938ec6859b2STodor Tomov ret = devm_request_irq(dev, ispif->irq, ispif_isr, 939ec6859b2STodor Tomov IRQF_TRIGGER_RISING, ispif->irq_name, ispif); 940ec6859b2STodor Tomov if (ret < 0) { 941ec6859b2STodor Tomov dev_err(dev, "request_irq failed: %d\n", ret); 942ec6859b2STodor Tomov return ret; 943ec6859b2STodor Tomov } 944ec6859b2STodor Tomov 945ec6859b2STodor Tomov /* Clocks */ 946ec6859b2STodor Tomov 947ec6859b2STodor Tomov ispif->nclocks = 0; 948ec6859b2STodor Tomov while (res->clock[ispif->nclocks]) 949ec6859b2STodor Tomov ispif->nclocks++; 950ec6859b2STodor Tomov 951ec6859b2STodor Tomov ispif->clock = devm_kcalloc(dev, 952ec6859b2STodor Tomov ispif->nclocks, sizeof(*ispif->clock), 953ec6859b2STodor Tomov GFP_KERNEL); 954ec6859b2STodor Tomov if (!ispif->clock) 955ec6859b2STodor Tomov return -ENOMEM; 956ec6859b2STodor Tomov 957ec6859b2STodor Tomov for (i = 0; i < ispif->nclocks; i++) { 958ec6859b2STodor Tomov struct camss_clock *clock = &ispif->clock[i]; 959ec6859b2STodor Tomov 960ec6859b2STodor Tomov clock->clk = devm_clk_get(dev, res->clock[i]); 961ec6859b2STodor Tomov if (IS_ERR(clock->clk)) 962ec6859b2STodor Tomov return PTR_ERR(clock->clk); 963ec6859b2STodor Tomov 964ec6859b2STodor Tomov clock->freq = NULL; 965ec6859b2STodor Tomov clock->nfreqs = 0; 966ec6859b2STodor Tomov } 967ec6859b2STodor Tomov 968ec6859b2STodor Tomov ispif->nclocks_for_reset = 0; 969ec6859b2STodor Tomov while (res->clock_for_reset[ispif->nclocks_for_reset]) 970ec6859b2STodor Tomov ispif->nclocks_for_reset++; 971ec6859b2STodor Tomov 972ec6859b2STodor Tomov ispif->clock_for_reset = devm_kcalloc(dev, 973ec6859b2STodor Tomov ispif->nclocks_for_reset, 974ec6859b2STodor Tomov sizeof(*ispif->clock_for_reset), 975ec6859b2STodor Tomov GFP_KERNEL); 976ec6859b2STodor Tomov if (!ispif->clock_for_reset) 977ec6859b2STodor Tomov return -ENOMEM; 978ec6859b2STodor Tomov 979ec6859b2STodor Tomov for (i = 0; i < ispif->nclocks_for_reset; i++) { 980ec6859b2STodor Tomov struct camss_clock *clock = &ispif->clock_for_reset[i]; 981ec6859b2STodor Tomov 982ec6859b2STodor Tomov clock->clk = devm_clk_get(dev, res->clock_for_reset[i]); 983ec6859b2STodor Tomov if (IS_ERR(clock->clk)) 984ec6859b2STodor Tomov return PTR_ERR(clock->clk); 985ec6859b2STodor Tomov 986ec6859b2STodor Tomov clock->freq = NULL; 987ec6859b2STodor Tomov clock->nfreqs = 0; 988ec6859b2STodor Tomov } 989ec6859b2STodor Tomov 990ec6859b2STodor Tomov for (i = 0; i < ARRAY_SIZE(ispif->line); i++) 991ec6859b2STodor Tomov ispif->line[i].id = i; 992ec6859b2STodor Tomov 993ec6859b2STodor Tomov mutex_init(&ispif->power_lock); 994ec6859b2STodor Tomov ispif->power_count = 0; 995ec6859b2STodor Tomov 996ec6859b2STodor Tomov mutex_init(&ispif->config_lock); 997ec6859b2STodor Tomov 998ec6859b2STodor Tomov init_completion(&ispif->reset_complete); 999ec6859b2STodor Tomov 1000ec6859b2STodor Tomov return 0; 1001ec6859b2STodor Tomov } 1002ec6859b2STodor Tomov 1003ec6859b2STodor Tomov /* 1004ec6859b2STodor Tomov * ispif_get_intf - Get ISPIF interface to use by VFE line id 1005ec6859b2STodor Tomov * @line_id: VFE line id that the ISPIF line is connected to 1006ec6859b2STodor Tomov * 1007ec6859b2STodor Tomov * Return ISPIF interface to use 1008ec6859b2STodor Tomov */ 1009ec6859b2STodor Tomov static enum ispif_intf ispif_get_intf(enum vfe_line_id line_id) 1010ec6859b2STodor Tomov { 1011ec6859b2STodor Tomov switch (line_id) { 1012ec6859b2STodor Tomov case (VFE_LINE_RDI0): 1013ec6859b2STodor Tomov return RDI0; 1014ec6859b2STodor Tomov case (VFE_LINE_RDI1): 1015ec6859b2STodor Tomov return RDI1; 1016ec6859b2STodor Tomov case (VFE_LINE_RDI2): 1017ec6859b2STodor Tomov return RDI2; 1018ec6859b2STodor Tomov case (VFE_LINE_PIX): 1019ec6859b2STodor Tomov return PIX0; 1020ec6859b2STodor Tomov default: 1021ec6859b2STodor Tomov return RDI0; 1022ec6859b2STodor Tomov } 1023ec6859b2STodor Tomov } 1024ec6859b2STodor Tomov 1025ec6859b2STodor Tomov /* 1026ec6859b2STodor Tomov * ispif_link_setup - Setup ISPIF connections 1027ec6859b2STodor Tomov * @entity: Pointer to media entity structure 1028ec6859b2STodor Tomov * @local: Pointer to local pad 1029ec6859b2STodor Tomov * @remote: Pointer to remote pad 1030ec6859b2STodor Tomov * @flags: Link flags 1031ec6859b2STodor Tomov * 1032ec6859b2STodor Tomov * Return 0 on success 1033ec6859b2STodor Tomov */ 1034ec6859b2STodor Tomov static int ispif_link_setup(struct media_entity *entity, 1035ec6859b2STodor Tomov const struct media_pad *local, 1036ec6859b2STodor Tomov const struct media_pad *remote, u32 flags) 1037ec6859b2STodor Tomov { 1038ec6859b2STodor Tomov if (flags & MEDIA_LNK_FL_ENABLED) { 1039ec6859b2STodor Tomov if (media_entity_remote_pad(local)) 1040ec6859b2STodor Tomov return -EBUSY; 1041ec6859b2STodor Tomov 1042ec6859b2STodor Tomov if (local->flags & MEDIA_PAD_FL_SINK) { 1043ec6859b2STodor Tomov struct v4l2_subdev *sd; 1044ec6859b2STodor Tomov struct ispif_line *line; 1045ec6859b2STodor Tomov 1046ec6859b2STodor Tomov sd = media_entity_to_v4l2_subdev(entity); 1047ec6859b2STodor Tomov line = v4l2_get_subdevdata(sd); 1048ec6859b2STodor Tomov 1049ec6859b2STodor Tomov msm_csid_get_csid_id(remote->entity, &line->csid_id); 1050ec6859b2STodor Tomov } else { /* MEDIA_PAD_FL_SOURCE */ 1051ec6859b2STodor Tomov struct v4l2_subdev *sd; 1052ec6859b2STodor Tomov struct ispif_line *line; 1053ec6859b2STodor Tomov enum vfe_line_id id; 1054ec6859b2STodor Tomov 1055ec6859b2STodor Tomov sd = media_entity_to_v4l2_subdev(entity); 1056ec6859b2STodor Tomov line = v4l2_get_subdevdata(sd); 1057ec6859b2STodor Tomov 1058ec6859b2STodor Tomov msm_vfe_get_vfe_id(remote->entity, &line->vfe_id); 1059ec6859b2STodor Tomov msm_vfe_get_vfe_line_id(remote->entity, &id); 1060ec6859b2STodor Tomov line->interface = ispif_get_intf(id); 1061ec6859b2STodor Tomov } 1062ec6859b2STodor Tomov } 1063ec6859b2STodor Tomov 1064ec6859b2STodor Tomov return 0; 1065ec6859b2STodor Tomov } 1066ec6859b2STodor Tomov 1067ec6859b2STodor Tomov static const struct v4l2_subdev_core_ops ispif_core_ops = { 1068ec6859b2STodor Tomov .s_power = ispif_set_power, 1069ec6859b2STodor Tomov }; 1070ec6859b2STodor Tomov 1071ec6859b2STodor Tomov static const struct v4l2_subdev_video_ops ispif_video_ops = { 1072ec6859b2STodor Tomov .s_stream = ispif_set_stream, 1073ec6859b2STodor Tomov }; 1074ec6859b2STodor Tomov 1075ec6859b2STodor Tomov static const struct v4l2_subdev_pad_ops ispif_pad_ops = { 1076ec6859b2STodor Tomov .enum_mbus_code = ispif_enum_mbus_code, 1077ec6859b2STodor Tomov .enum_frame_size = ispif_enum_frame_size, 1078ec6859b2STodor Tomov .get_fmt = ispif_get_format, 1079ec6859b2STodor Tomov .set_fmt = ispif_set_format, 1080ec6859b2STodor Tomov }; 1081ec6859b2STodor Tomov 1082ec6859b2STodor Tomov static const struct v4l2_subdev_ops ispif_v4l2_ops = { 1083ec6859b2STodor Tomov .core = &ispif_core_ops, 1084ec6859b2STodor Tomov .video = &ispif_video_ops, 1085ec6859b2STodor Tomov .pad = &ispif_pad_ops, 1086ec6859b2STodor Tomov }; 1087ec6859b2STodor Tomov 1088ec6859b2STodor Tomov static const struct v4l2_subdev_internal_ops ispif_v4l2_internal_ops = { 1089ec6859b2STodor Tomov .open = ispif_init_formats, 1090ec6859b2STodor Tomov }; 1091ec6859b2STodor Tomov 1092ec6859b2STodor Tomov static const struct media_entity_operations ispif_media_ops = { 1093ec6859b2STodor Tomov .link_setup = ispif_link_setup, 1094ec6859b2STodor Tomov .link_validate = v4l2_subdev_link_validate, 1095ec6859b2STodor Tomov }; 1096ec6859b2STodor Tomov 1097ec6859b2STodor Tomov /* 1098ec6859b2STodor Tomov * msm_ispif_register_entities - Register subdev node for ISPIF module 1099ec6859b2STodor Tomov * @ispif: ISPIF device 1100ec6859b2STodor Tomov * @v4l2_dev: V4L2 device 1101ec6859b2STodor Tomov * 1102ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 1103ec6859b2STodor Tomov */ 1104ec6859b2STodor Tomov int msm_ispif_register_entities(struct ispif_device *ispif, 1105ec6859b2STodor Tomov struct v4l2_device *v4l2_dev) 1106ec6859b2STodor Tomov { 1107ec6859b2STodor Tomov struct device *dev = to_device(ispif); 1108ec6859b2STodor Tomov int ret; 1109ec6859b2STodor Tomov int i; 1110ec6859b2STodor Tomov 1111ec6859b2STodor Tomov for (i = 0; i < ARRAY_SIZE(ispif->line); i++) { 1112ec6859b2STodor Tomov struct v4l2_subdev *sd = &ispif->line[i].subdev; 1113ec6859b2STodor Tomov struct media_pad *pads = ispif->line[i].pads; 1114ec6859b2STodor Tomov 1115ec6859b2STodor Tomov v4l2_subdev_init(sd, &ispif_v4l2_ops); 1116ec6859b2STodor Tomov sd->internal_ops = &ispif_v4l2_internal_ops; 1117ec6859b2STodor Tomov sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 1118ec6859b2STodor Tomov snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", 1119ec6859b2STodor Tomov MSM_ISPIF_NAME, i); 1120ec6859b2STodor Tomov v4l2_set_subdevdata(sd, &ispif->line[i]); 1121ec6859b2STodor Tomov 1122ec6859b2STodor Tomov ret = ispif_init_formats(sd, NULL); 1123ec6859b2STodor Tomov if (ret < 0) { 1124ec6859b2STodor Tomov dev_err(dev, "Failed to init format: %d\n", ret); 1125ec6859b2STodor Tomov goto error; 1126ec6859b2STodor Tomov } 1127ec6859b2STodor Tomov 1128ec6859b2STodor Tomov pads[MSM_ISPIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 1129ec6859b2STodor Tomov pads[MSM_ISPIF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; 1130ec6859b2STodor Tomov 1131ec6859b2STodor Tomov sd->entity.function = MEDIA_ENT_F_IO_V4L; 1132ec6859b2STodor Tomov sd->entity.ops = &ispif_media_ops; 1133ec6859b2STodor Tomov ret = media_entity_pads_init(&sd->entity, MSM_ISPIF_PADS_NUM, 1134ec6859b2STodor Tomov pads); 1135ec6859b2STodor Tomov if (ret < 0) { 1136ec6859b2STodor Tomov dev_err(dev, "Failed to init media entity: %d\n", ret); 1137ec6859b2STodor Tomov goto error; 1138ec6859b2STodor Tomov } 1139ec6859b2STodor Tomov 1140ec6859b2STodor Tomov ret = v4l2_device_register_subdev(v4l2_dev, sd); 1141ec6859b2STodor Tomov if (ret < 0) { 1142ec6859b2STodor Tomov dev_err(dev, "Failed to register subdev: %d\n", ret); 1143ec6859b2STodor Tomov media_entity_cleanup(&sd->entity); 1144ec6859b2STodor Tomov goto error; 1145ec6859b2STodor Tomov } 1146ec6859b2STodor Tomov } 1147ec6859b2STodor Tomov 1148ec6859b2STodor Tomov return 0; 1149ec6859b2STodor Tomov 1150ec6859b2STodor Tomov error: 1151ec6859b2STodor Tomov for (i--; i >= 0; i--) { 1152ec6859b2STodor Tomov struct v4l2_subdev *sd = &ispif->line[i].subdev; 1153ec6859b2STodor Tomov 1154ec6859b2STodor Tomov v4l2_device_unregister_subdev(sd); 1155ec6859b2STodor Tomov media_entity_cleanup(&sd->entity); 1156ec6859b2STodor Tomov } 1157ec6859b2STodor Tomov 1158ec6859b2STodor Tomov return ret; 1159ec6859b2STodor Tomov } 1160ec6859b2STodor Tomov 1161ec6859b2STodor Tomov /* 1162ec6859b2STodor Tomov * msm_ispif_unregister_entities - Unregister ISPIF module subdev node 1163ec6859b2STodor Tomov * @ispif: ISPIF device 1164ec6859b2STodor Tomov */ 1165ec6859b2STodor Tomov void msm_ispif_unregister_entities(struct ispif_device *ispif) 1166ec6859b2STodor Tomov { 1167ec6859b2STodor Tomov int i; 1168ec6859b2STodor Tomov 1169ec6859b2STodor Tomov mutex_destroy(&ispif->power_lock); 1170ec6859b2STodor Tomov mutex_destroy(&ispif->config_lock); 1171ec6859b2STodor Tomov 1172ec6859b2STodor Tomov for (i = 0; i < ARRAY_SIZE(ispif->line); i++) { 1173ec6859b2STodor Tomov struct v4l2_subdev *sd = &ispif->line[i].subdev; 1174ec6859b2STodor Tomov 1175ec6859b2STodor Tomov v4l2_device_unregister_subdev(sd); 1176ec6859b2STodor Tomov media_entity_cleanup(&sd->entity); 1177ec6859b2STodor Tomov } 1178ec6859b2STodor Tomov } 1179