1b873663bSTodor Tomov // SPDX-License-Identifier: GPL-2.0 2ec6859b2STodor Tomov /* 3ec6859b2STodor Tomov * camss-csid.c 4ec6859b2STodor Tomov * 5ec6859b2STodor Tomov * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module 6ec6859b2STodor Tomov * 7ec6859b2STodor Tomov * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. 8ec6859b2STodor Tomov * Copyright (C) 2015-2018 Linaro Ltd. 9ec6859b2STodor Tomov */ 10ec6859b2STodor Tomov #include <linux/clk.h> 11ec6859b2STodor Tomov #include <linux/completion.h> 12ec6859b2STodor Tomov #include <linux/interrupt.h> 13ec6859b2STodor Tomov #include <linux/kernel.h> 14ec6859b2STodor Tomov #include <linux/of.h> 15ec6859b2STodor Tomov #include <linux/platform_device.h> 1602afa816STodor Tomov #include <linux/pm_runtime.h> 17ec6859b2STodor Tomov #include <linux/regulator/consumer.h> 18ec6859b2STodor Tomov #include <media/media-entity.h> 19ec6859b2STodor Tomov #include <media/v4l2-device.h> 20ec6859b2STodor Tomov #include <media/v4l2-subdev.h> 21ec6859b2STodor Tomov 22ec6859b2STodor Tomov #include "camss-csid.h" 23ec6859b2STodor Tomov #include "camss.h" 24ec6859b2STodor Tomov 25ec6859b2STodor Tomov #define MSM_CSID_NAME "msm_csid" 26ec6859b2STodor Tomov 27ec6859b2STodor Tomov #define CAMSS_CSID_HW_VERSION 0x0 28ec6859b2STodor Tomov #define CAMSS_CSID_CORE_CTRL_0 0x004 29ec6859b2STodor Tomov #define CAMSS_CSID_CORE_CTRL_1 0x008 30*2a05493bSTodor Tomov #define CAMSS_CSID_RST_CMD(v) ((v) == CAMSS_8x16 ? 0x00c : 0x010) 31*2a05493bSTodor Tomov #define CAMSS_CSID_CID_LUT_VC_n(v, n) \ 32*2a05493bSTodor Tomov (((v) == CAMSS_8x16 ? 0x010 : 0x014) + 0x4 * (n)) 33*2a05493bSTodor Tomov #define CAMSS_CSID_CID_n_CFG(v, n) \ 34*2a05493bSTodor Tomov (((v) == CAMSS_8x16 ? 0x020 : 0x024) + 0x4 * (n)) 35*2a05493bSTodor Tomov #define CAMSS_CSID_IRQ_CLEAR_CMD(v) ((v) == CAMSS_8x16 ? 0x060 : 0x064) 36*2a05493bSTodor Tomov #define CAMSS_CSID_IRQ_MASK(v) ((v) == CAMSS_8x16 ? 0x064 : 0x068) 37*2a05493bSTodor Tomov #define CAMSS_CSID_IRQ_STATUS(v) ((v) == CAMSS_8x16 ? 0x068 : 0x06c) 38*2a05493bSTodor Tomov #define CAMSS_CSID_TG_CTRL(v) ((v) == CAMSS_8x16 ? 0x0a0 : 0x0a8) 39ec6859b2STodor Tomov #define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436 40ec6859b2STodor Tomov #define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437 41*2a05493bSTodor Tomov #define CAMSS_CSID_TG_VC_CFG(v) ((v) == CAMSS_8x16 ? 0x0a4 : 0x0ac) 42ec6859b2STodor Tomov #define CAMSS_CSID_TG_VC_CFG_H_BLANKING 0x3ff 43ec6859b2STodor Tomov #define CAMSS_CSID_TG_VC_CFG_V_BLANKING 0x7f 44*2a05493bSTodor Tomov #define CAMSS_CSID_TG_DT_n_CGG_0(v, n) \ 45*2a05493bSTodor Tomov (((v) == CAMSS_8x16 ? 0x0ac : 0x0b4) + 0xc * (n)) 46*2a05493bSTodor Tomov #define CAMSS_CSID_TG_DT_n_CGG_1(v, n) \ 47*2a05493bSTodor Tomov (((v) == CAMSS_8x16 ? 0x0b0 : 0x0b8) + 0xc * (n)) 48*2a05493bSTodor Tomov #define CAMSS_CSID_TG_DT_n_CGG_2(v, n) \ 49*2a05493bSTodor Tomov (((v) == CAMSS_8x16 ? 0x0b4 : 0x0bc) + 0xc * (n)) 50ec6859b2STodor Tomov 51ec6859b2STodor Tomov #define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12 52ec6859b2STodor Tomov #define DATA_TYPE_YUV422_8BIT 0x1e 53ec6859b2STodor Tomov #define DATA_TYPE_RAW_6BIT 0x28 54ec6859b2STodor Tomov #define DATA_TYPE_RAW_8BIT 0x2a 55ec6859b2STodor Tomov #define DATA_TYPE_RAW_10BIT 0x2b 56ec6859b2STodor Tomov #define DATA_TYPE_RAW_12BIT 0x2c 57ec6859b2STodor Tomov 58ec6859b2STodor Tomov #define DECODE_FORMAT_UNCOMPRESSED_6_BIT 0x0 59ec6859b2STodor Tomov #define DECODE_FORMAT_UNCOMPRESSED_8_BIT 0x1 60ec6859b2STodor Tomov #define DECODE_FORMAT_UNCOMPRESSED_10_BIT 0x2 61ec6859b2STodor Tomov #define DECODE_FORMAT_UNCOMPRESSED_12_BIT 0x3 62ec6859b2STodor Tomov 63ec6859b2STodor Tomov #define CSID_RESET_TIMEOUT_MS 500 64ec6859b2STodor Tomov 65ec6859b2STodor Tomov struct csid_fmts { 66ec6859b2STodor Tomov u32 code; 67ec6859b2STodor Tomov u8 data_type; 68ec6859b2STodor Tomov u8 decode_format; 69ec6859b2STodor Tomov u8 bpp; 70ec6859b2STodor Tomov u8 spp; /* bus samples per pixel */ 71ec6859b2STodor Tomov }; 72ec6859b2STodor Tomov 73ec6859b2STodor Tomov static const struct csid_fmts csid_input_fmts[] = { 74ec6859b2STodor Tomov { 75ec6859b2STodor Tomov MEDIA_BUS_FMT_UYVY8_2X8, 76ec6859b2STodor Tomov DATA_TYPE_YUV422_8BIT, 77ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 78ec6859b2STodor Tomov 8, 79ec6859b2STodor Tomov 2, 80ec6859b2STodor Tomov }, 81ec6859b2STodor Tomov { 82ec6859b2STodor Tomov MEDIA_BUS_FMT_VYUY8_2X8, 83ec6859b2STodor Tomov DATA_TYPE_YUV422_8BIT, 84ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 85ec6859b2STodor Tomov 8, 86ec6859b2STodor Tomov 2, 87ec6859b2STodor Tomov }, 88ec6859b2STodor Tomov { 89ec6859b2STodor Tomov MEDIA_BUS_FMT_YUYV8_2X8, 90ec6859b2STodor Tomov DATA_TYPE_YUV422_8BIT, 91ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 92ec6859b2STodor Tomov 8, 93ec6859b2STodor Tomov 2, 94ec6859b2STodor Tomov }, 95ec6859b2STodor Tomov { 96ec6859b2STodor Tomov MEDIA_BUS_FMT_YVYU8_2X8, 97ec6859b2STodor Tomov DATA_TYPE_YUV422_8BIT, 98ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 99ec6859b2STodor Tomov 8, 100ec6859b2STodor Tomov 2, 101ec6859b2STodor Tomov }, 102ec6859b2STodor Tomov { 103ec6859b2STodor Tomov MEDIA_BUS_FMT_SBGGR8_1X8, 104ec6859b2STodor Tomov DATA_TYPE_RAW_8BIT, 105ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 106ec6859b2STodor Tomov 8, 107ec6859b2STodor Tomov 1, 108ec6859b2STodor Tomov }, 109ec6859b2STodor Tomov { 110ec6859b2STodor Tomov MEDIA_BUS_FMT_SGBRG8_1X8, 111ec6859b2STodor Tomov DATA_TYPE_RAW_8BIT, 112ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 113ec6859b2STodor Tomov 8, 114ec6859b2STodor Tomov 1, 115ec6859b2STodor Tomov }, 116ec6859b2STodor Tomov { 117ec6859b2STodor Tomov MEDIA_BUS_FMT_SGRBG8_1X8, 118ec6859b2STodor Tomov DATA_TYPE_RAW_8BIT, 119ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 120ec6859b2STodor Tomov 8, 121ec6859b2STodor Tomov 1, 122ec6859b2STodor Tomov }, 123ec6859b2STodor Tomov { 124ec6859b2STodor Tomov MEDIA_BUS_FMT_SRGGB8_1X8, 125ec6859b2STodor Tomov DATA_TYPE_RAW_8BIT, 126ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 127ec6859b2STodor Tomov 8, 128ec6859b2STodor Tomov 1, 129ec6859b2STodor Tomov }, 130ec6859b2STodor Tomov { 131ec6859b2STodor Tomov MEDIA_BUS_FMT_SBGGR10_1X10, 132ec6859b2STodor Tomov DATA_TYPE_RAW_10BIT, 133ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 134ec6859b2STodor Tomov 10, 135ec6859b2STodor Tomov 1, 136ec6859b2STodor Tomov }, 137ec6859b2STodor Tomov { 138ec6859b2STodor Tomov MEDIA_BUS_FMT_SGBRG10_1X10, 139ec6859b2STodor Tomov DATA_TYPE_RAW_10BIT, 140ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 141ec6859b2STodor Tomov 10, 142ec6859b2STodor Tomov 1, 143ec6859b2STodor Tomov }, 144ec6859b2STodor Tomov { 145ec6859b2STodor Tomov MEDIA_BUS_FMT_SGRBG10_1X10, 146ec6859b2STodor Tomov DATA_TYPE_RAW_10BIT, 147ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 148ec6859b2STodor Tomov 10, 149ec6859b2STodor Tomov 1, 150ec6859b2STodor Tomov }, 151ec6859b2STodor Tomov { 152ec6859b2STodor Tomov MEDIA_BUS_FMT_SRGGB10_1X10, 153ec6859b2STodor Tomov DATA_TYPE_RAW_10BIT, 154ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 155ec6859b2STodor Tomov 10, 156ec6859b2STodor Tomov 1, 157ec6859b2STodor Tomov }, 158ec6859b2STodor Tomov { 159ec6859b2STodor Tomov MEDIA_BUS_FMT_SBGGR12_1X12, 160ec6859b2STodor Tomov DATA_TYPE_RAW_12BIT, 161ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_12_BIT, 162ec6859b2STodor Tomov 12, 163ec6859b2STodor Tomov 1, 164ec6859b2STodor Tomov }, 165ec6859b2STodor Tomov { 166ec6859b2STodor Tomov MEDIA_BUS_FMT_SGBRG12_1X12, 167ec6859b2STodor Tomov DATA_TYPE_RAW_12BIT, 168ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_12_BIT, 169ec6859b2STodor Tomov 12, 170ec6859b2STodor Tomov 1, 171ec6859b2STodor Tomov }, 172ec6859b2STodor Tomov { 173ec6859b2STodor Tomov MEDIA_BUS_FMT_SGRBG12_1X12, 174ec6859b2STodor Tomov DATA_TYPE_RAW_12BIT, 175ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_12_BIT, 176ec6859b2STodor Tomov 12, 177ec6859b2STodor Tomov 1, 178ec6859b2STodor Tomov }, 179ec6859b2STodor Tomov { 180ec6859b2STodor Tomov MEDIA_BUS_FMT_SRGGB12_1X12, 181ec6859b2STodor Tomov DATA_TYPE_RAW_12BIT, 182ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_12_BIT, 183ec6859b2STodor Tomov 12, 184ec6859b2STodor Tomov 1, 185ec6859b2STodor Tomov } 186ec6859b2STodor Tomov }; 187ec6859b2STodor Tomov 188ec6859b2STodor Tomov static const struct csid_fmts *csid_get_fmt_entry(u32 code) 189ec6859b2STodor Tomov { 190ec6859b2STodor Tomov unsigned int i; 191ec6859b2STodor Tomov 192ec6859b2STodor Tomov for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) 193ec6859b2STodor Tomov if (code == csid_input_fmts[i].code) 194ec6859b2STodor Tomov return &csid_input_fmts[i]; 195ec6859b2STodor Tomov 196ec6859b2STodor Tomov WARN(1, "Unknown format\n"); 197ec6859b2STodor Tomov 198ec6859b2STodor Tomov return &csid_input_fmts[0]; 199ec6859b2STodor Tomov } 200ec6859b2STodor Tomov 201ec6859b2STodor Tomov /* 202ec6859b2STodor Tomov * csid_isr - CSID module interrupt handler 203ec6859b2STodor Tomov * @irq: Interrupt line 204ec6859b2STodor Tomov * @dev: CSID device 205ec6859b2STodor Tomov * 206ec6859b2STodor Tomov * Return IRQ_HANDLED on success 207ec6859b2STodor Tomov */ 208ec6859b2STodor Tomov static irqreturn_t csid_isr(int irq, void *dev) 209ec6859b2STodor Tomov { 210ec6859b2STodor Tomov struct csid_device *csid = dev; 211*2a05493bSTodor Tomov enum camss_version ver = csid->camss->version; 212ec6859b2STodor Tomov u32 value; 213ec6859b2STodor Tomov 214*2a05493bSTodor Tomov value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS(ver)); 215*2a05493bSTodor Tomov writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD(ver)); 216ec6859b2STodor Tomov 217ec6859b2STodor Tomov if ((value >> 11) & 0x1) 218ec6859b2STodor Tomov complete(&csid->reset_complete); 219ec6859b2STodor Tomov 220ec6859b2STodor Tomov return IRQ_HANDLED; 221ec6859b2STodor Tomov } 222ec6859b2STodor Tomov 223ec6859b2STodor Tomov /* 224ec6859b2STodor Tomov * csid_set_clock_rates - Calculate and set clock rates on CSID module 225ec6859b2STodor Tomov * @csiphy: CSID device 226ec6859b2STodor Tomov */ 227ec6859b2STodor Tomov static int csid_set_clock_rates(struct csid_device *csid) 228ec6859b2STodor Tomov { 2299c3e59deSTodor Tomov struct device *dev = csid->camss->dev; 230ec6859b2STodor Tomov u32 pixel_clock; 231ec6859b2STodor Tomov int i, j; 232ec6859b2STodor Tomov int ret; 233ec6859b2STodor Tomov 234ec6859b2STodor Tomov ret = camss_get_pixel_clock(&csid->subdev.entity, &pixel_clock); 235ec6859b2STodor Tomov if (ret) 236ec6859b2STodor Tomov pixel_clock = 0; 237ec6859b2STodor Tomov 238ec6859b2STodor Tomov for (i = 0; i < csid->nclocks; i++) { 239ec6859b2STodor Tomov struct camss_clock *clock = &csid->clock[i]; 240ec6859b2STodor Tomov 241ec6859b2STodor Tomov if (!strcmp(clock->name, "csi0") || 2429c3e59deSTodor Tomov !strcmp(clock->name, "csi1") || 2439c3e59deSTodor Tomov !strcmp(clock->name, "csi2") || 2449c3e59deSTodor Tomov !strcmp(clock->name, "csi3")) { 245ec6859b2STodor Tomov u8 bpp = csid_get_fmt_entry( 246ec6859b2STodor Tomov csid->fmt[MSM_CSIPHY_PAD_SINK].code)->bpp; 247ec6859b2STodor Tomov u8 num_lanes = csid->phy.lane_cnt; 248ec6859b2STodor Tomov u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4); 249ec6859b2STodor Tomov long rate; 250ec6859b2STodor Tomov 251ec6859b2STodor Tomov camss_add_clock_margin(&min_rate); 252ec6859b2STodor Tomov 253ec6859b2STodor Tomov for (j = 0; j < clock->nfreqs; j++) 254ec6859b2STodor Tomov if (min_rate < clock->freq[j]) 255ec6859b2STodor Tomov break; 256ec6859b2STodor Tomov 257ec6859b2STodor Tomov if (j == clock->nfreqs) { 258ec6859b2STodor Tomov dev_err(dev, 259ec6859b2STodor Tomov "Pixel clock is too high for CSID\n"); 260ec6859b2STodor Tomov return -EINVAL; 261ec6859b2STodor Tomov } 262ec6859b2STodor Tomov 263ec6859b2STodor Tomov /* if sensor pixel clock is not available */ 264ec6859b2STodor Tomov /* set highest possible CSID clock rate */ 265ec6859b2STodor Tomov if (min_rate == 0) 266ec6859b2STodor Tomov j = clock->nfreqs - 1; 267ec6859b2STodor Tomov 268ec6859b2STodor Tomov rate = clk_round_rate(clock->clk, clock->freq[j]); 269ec6859b2STodor Tomov if (rate < 0) { 270ec6859b2STodor Tomov dev_err(dev, "clk round rate failed: %ld\n", 271ec6859b2STodor Tomov rate); 272ec6859b2STodor Tomov return -EINVAL; 273ec6859b2STodor Tomov } 274ec6859b2STodor Tomov 275ec6859b2STodor Tomov ret = clk_set_rate(clock->clk, rate); 276ec6859b2STodor Tomov if (ret < 0) { 277ec6859b2STodor Tomov dev_err(dev, "clk set rate failed: %d\n", ret); 278ec6859b2STodor Tomov return ret; 279ec6859b2STodor Tomov } 280ec6859b2STodor Tomov } 281ec6859b2STodor Tomov } 282ec6859b2STodor Tomov 283ec6859b2STodor Tomov return 0; 284ec6859b2STodor Tomov } 285ec6859b2STodor Tomov 286ec6859b2STodor Tomov /* 287ec6859b2STodor Tomov * csid_reset - Trigger reset on CSID module and wait to complete 288ec6859b2STodor Tomov * @csid: CSID device 289ec6859b2STodor Tomov * 290ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 291ec6859b2STodor Tomov */ 292ec6859b2STodor Tomov static int csid_reset(struct csid_device *csid) 293ec6859b2STodor Tomov { 294ec6859b2STodor Tomov unsigned long time; 295ec6859b2STodor Tomov 296ec6859b2STodor Tomov reinit_completion(&csid->reset_complete); 297ec6859b2STodor Tomov 298*2a05493bSTodor Tomov writel_relaxed(0x7fff, csid->base + 299*2a05493bSTodor Tomov CAMSS_CSID_RST_CMD(csid->camss->version)); 300ec6859b2STodor Tomov 301ec6859b2STodor Tomov time = wait_for_completion_timeout(&csid->reset_complete, 302ec6859b2STodor Tomov msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); 303ec6859b2STodor Tomov if (!time) { 3049c3e59deSTodor Tomov dev_err(csid->camss->dev, "CSID reset timeout\n"); 305ec6859b2STodor Tomov return -EIO; 306ec6859b2STodor Tomov } 307ec6859b2STodor Tomov 308ec6859b2STodor Tomov return 0; 309ec6859b2STodor Tomov } 310ec6859b2STodor Tomov 311ec6859b2STodor Tomov /* 312ec6859b2STodor Tomov * csid_set_power - Power on/off CSID module 313ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 314ec6859b2STodor Tomov * @on: Requested power state 315ec6859b2STodor Tomov * 316ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 317ec6859b2STodor Tomov */ 318ec6859b2STodor Tomov static int csid_set_power(struct v4l2_subdev *sd, int on) 319ec6859b2STodor Tomov { 320ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 3219c3e59deSTodor Tomov struct device *dev = csid->camss->dev; 322ec6859b2STodor Tomov int ret; 323ec6859b2STodor Tomov 324ec6859b2STodor Tomov if (on) { 325ec6859b2STodor Tomov u32 hw_version; 326ec6859b2STodor Tomov 32702afa816STodor Tomov ret = pm_runtime_get_sync(dev); 328ec6859b2STodor Tomov if (ret < 0) 329ec6859b2STodor Tomov return ret; 330ec6859b2STodor Tomov 33102afa816STodor Tomov ret = regulator_enable(csid->vdda); 33202afa816STodor Tomov if (ret < 0) { 33302afa816STodor Tomov pm_runtime_put_sync(dev); 33402afa816STodor Tomov return ret; 33502afa816STodor Tomov } 33602afa816STodor Tomov 337ec6859b2STodor Tomov ret = csid_set_clock_rates(csid); 338ec6859b2STodor Tomov if (ret < 0) { 339ec6859b2STodor Tomov regulator_disable(csid->vdda); 34002afa816STodor Tomov pm_runtime_put_sync(dev); 341ec6859b2STodor Tomov return ret; 342ec6859b2STodor Tomov } 343ec6859b2STodor Tomov 344ec6859b2STodor Tomov ret = camss_enable_clocks(csid->nclocks, csid->clock, dev); 345ec6859b2STodor Tomov if (ret < 0) { 346ec6859b2STodor Tomov regulator_disable(csid->vdda); 34702afa816STodor Tomov pm_runtime_put_sync(dev); 348ec6859b2STodor Tomov return ret; 349ec6859b2STodor Tomov } 350ec6859b2STodor Tomov 351ec6859b2STodor Tomov enable_irq(csid->irq); 352ec6859b2STodor Tomov 353ec6859b2STodor Tomov ret = csid_reset(csid); 354ec6859b2STodor Tomov if (ret < 0) { 355ec6859b2STodor Tomov disable_irq(csid->irq); 356ec6859b2STodor Tomov camss_disable_clocks(csid->nclocks, csid->clock); 357ec6859b2STodor Tomov regulator_disable(csid->vdda); 35802afa816STodor Tomov pm_runtime_put_sync(dev); 359ec6859b2STodor Tomov return ret; 360ec6859b2STodor Tomov } 361ec6859b2STodor Tomov 362ec6859b2STodor Tomov hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION); 363ec6859b2STodor Tomov dev_dbg(dev, "CSID HW Version = 0x%08x\n", hw_version); 364ec6859b2STodor Tomov } else { 365ec6859b2STodor Tomov disable_irq(csid->irq); 366ec6859b2STodor Tomov camss_disable_clocks(csid->nclocks, csid->clock); 367ec6859b2STodor Tomov ret = regulator_disable(csid->vdda); 36802afa816STodor Tomov pm_runtime_put_sync(dev); 369ec6859b2STodor Tomov } 370ec6859b2STodor Tomov 371ec6859b2STodor Tomov return ret; 372ec6859b2STodor Tomov } 373ec6859b2STodor Tomov 374ec6859b2STodor Tomov /* 375ec6859b2STodor Tomov * csid_set_stream - Enable/disable streaming on CSID module 376ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 377ec6859b2STodor Tomov * @enable: Requested streaming state 378ec6859b2STodor Tomov * 379ec6859b2STodor Tomov * Main configuration of CSID module is also done here. 380ec6859b2STodor Tomov * 381ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 382ec6859b2STodor Tomov */ 383ec6859b2STodor Tomov static int csid_set_stream(struct v4l2_subdev *sd, int enable) 384ec6859b2STodor Tomov { 385ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 386ec6859b2STodor Tomov struct csid_testgen_config *tg = &csid->testgen; 387*2a05493bSTodor Tomov enum camss_version ver = csid->camss->version; 388ec6859b2STodor Tomov u32 val; 389ec6859b2STodor Tomov 390ec6859b2STodor Tomov if (enable) { 391ec6859b2STodor Tomov u8 vc = 0; /* Virtual Channel 0 */ 392ec6859b2STodor Tomov u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ 393ec6859b2STodor Tomov u8 dt, dt_shift, df; 394ec6859b2STodor Tomov int ret; 395ec6859b2STodor Tomov 396ec6859b2STodor Tomov ret = v4l2_ctrl_handler_setup(&csid->ctrls); 397ec6859b2STodor Tomov if (ret < 0) { 3989c3e59deSTodor Tomov dev_err(csid->camss->dev, 399ec6859b2STodor Tomov "could not sync v4l2 controls: %d\n", ret); 400ec6859b2STodor Tomov return ret; 401ec6859b2STodor Tomov } 402ec6859b2STodor Tomov 403ec6859b2STodor Tomov if (!tg->enabled && 404ec6859b2STodor Tomov !media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) 405ec6859b2STodor Tomov return -ENOLINK; 406ec6859b2STodor Tomov 407ec6859b2STodor Tomov if (tg->enabled) { 408ec6859b2STodor Tomov /* Config Test Generator */ 409ec6859b2STodor Tomov struct v4l2_mbus_framefmt *f = 410ec6859b2STodor Tomov &csid->fmt[MSM_CSID_PAD_SRC]; 411ec6859b2STodor Tomov u8 bpp = csid_get_fmt_entry(f->code)->bpp; 412ec6859b2STodor Tomov u8 spp = csid_get_fmt_entry(f->code)->spp; 413ec6859b2STodor Tomov u32 num_bytes_per_line = f->width * bpp * spp / 8; 414ec6859b2STodor Tomov u32 num_lines = f->height; 415ec6859b2STodor Tomov 416ec6859b2STodor Tomov /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */ 417ec6859b2STodor Tomov /* 1:0 VC */ 418ec6859b2STodor Tomov val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) | 419ec6859b2STodor Tomov ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13); 420*2a05493bSTodor Tomov writel_relaxed(val, csid->base + 421*2a05493bSTodor Tomov CAMSS_CSID_TG_VC_CFG(ver)); 422ec6859b2STodor Tomov 423ec6859b2STodor Tomov /* 28:16 bytes per lines, 12:0 num of lines */ 424ec6859b2STodor Tomov val = ((num_bytes_per_line & 0x1fff) << 16) | 425ec6859b2STodor Tomov (num_lines & 0x1fff); 426ec6859b2STodor Tomov writel_relaxed(val, csid->base + 427*2a05493bSTodor Tomov CAMSS_CSID_TG_DT_n_CGG_0(ver, 0)); 428ec6859b2STodor Tomov 429c628e788STodor Tomov dt = csid_get_fmt_entry( 430c628e788STodor Tomov csid->fmt[MSM_CSID_PAD_SRC].code)->data_type; 431c628e788STodor Tomov 432ec6859b2STodor Tomov /* 5:0 data type */ 433ec6859b2STodor Tomov val = dt; 434ec6859b2STodor Tomov writel_relaxed(val, csid->base + 435*2a05493bSTodor Tomov CAMSS_CSID_TG_DT_n_CGG_1(ver, 0)); 436ec6859b2STodor Tomov 437ec6859b2STodor Tomov /* 2:0 output test pattern */ 438ec6859b2STodor Tomov val = tg->payload_mode; 439ec6859b2STodor Tomov writel_relaxed(val, csid->base + 440*2a05493bSTodor Tomov CAMSS_CSID_TG_DT_n_CGG_2(ver, 0)); 441c628e788STodor Tomov 442c628e788STodor Tomov df = csid_get_fmt_entry( 443c628e788STodor Tomov csid->fmt[MSM_CSID_PAD_SRC].code)->decode_format; 444ec6859b2STodor Tomov } else { 445ec6859b2STodor Tomov struct csid_phy_config *phy = &csid->phy; 446ec6859b2STodor Tomov 447ec6859b2STodor Tomov val = phy->lane_cnt - 1; 448ec6859b2STodor Tomov val |= phy->lane_assign << 4; 449ec6859b2STodor Tomov 450ec6859b2STodor Tomov writel_relaxed(val, 451ec6859b2STodor Tomov csid->base + CAMSS_CSID_CORE_CTRL_0); 452ec6859b2STodor Tomov 453ec6859b2STodor Tomov val = phy->csiphy_id << 17; 454ec6859b2STodor Tomov val |= 0x9; 455ec6859b2STodor Tomov 456ec6859b2STodor Tomov writel_relaxed(val, 457ec6859b2STodor Tomov csid->base + CAMSS_CSID_CORE_CTRL_1); 458c628e788STodor Tomov 459c628e788STodor Tomov dt = csid_get_fmt_entry( 460c628e788STodor Tomov csid->fmt[MSM_CSID_PAD_SINK].code)->data_type; 461c628e788STodor Tomov df = csid_get_fmt_entry( 462c628e788STodor Tomov csid->fmt[MSM_CSID_PAD_SINK].code)->decode_format; 463ec6859b2STodor Tomov } 464ec6859b2STodor Tomov 465ec6859b2STodor Tomov /* Config LUT */ 466ec6859b2STodor Tomov 467ec6859b2STodor Tomov dt_shift = (cid % 4) * 8; 468ec6859b2STodor Tomov 469*2a05493bSTodor Tomov val = readl_relaxed(csid->base + 470*2a05493bSTodor Tomov CAMSS_CSID_CID_LUT_VC_n(ver, vc)); 471ec6859b2STodor Tomov val &= ~(0xff << dt_shift); 472ec6859b2STodor Tomov val |= dt << dt_shift; 473*2a05493bSTodor Tomov writel_relaxed(val, csid->base + 474*2a05493bSTodor Tomov CAMSS_CSID_CID_LUT_VC_n(ver, vc)); 475ec6859b2STodor Tomov 476ec6859b2STodor Tomov val = (df << 4) | 0x3; 477*2a05493bSTodor Tomov writel_relaxed(val, csid->base + 478*2a05493bSTodor Tomov CAMSS_CSID_CID_n_CFG(ver, cid)); 479ec6859b2STodor Tomov 480ec6859b2STodor Tomov if (tg->enabled) { 481ec6859b2STodor Tomov val = CAMSS_CSID_TG_CTRL_ENABLE; 482*2a05493bSTodor Tomov writel_relaxed(val, csid->base + 483*2a05493bSTodor Tomov CAMSS_CSID_TG_CTRL(ver)); 484ec6859b2STodor Tomov } 485ec6859b2STodor Tomov } else { 486ec6859b2STodor Tomov if (tg->enabled) { 487ec6859b2STodor Tomov val = CAMSS_CSID_TG_CTRL_DISABLE; 488*2a05493bSTodor Tomov writel_relaxed(val, csid->base + 489*2a05493bSTodor Tomov CAMSS_CSID_TG_CTRL(ver)); 490ec6859b2STodor Tomov } 491ec6859b2STodor Tomov } 492ec6859b2STodor Tomov 493ec6859b2STodor Tomov return 0; 494ec6859b2STodor Tomov } 495ec6859b2STodor Tomov 496ec6859b2STodor Tomov /* 497ec6859b2STodor Tomov * __csid_get_format - Get pointer to format structure 498ec6859b2STodor Tomov * @csid: CSID device 499ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 500ec6859b2STodor Tomov * @pad: pad from which format is requested 501ec6859b2STodor Tomov * @which: TRY or ACTIVE format 502ec6859b2STodor Tomov * 503ec6859b2STodor Tomov * Return pointer to TRY or ACTIVE format structure 504ec6859b2STodor Tomov */ 505ec6859b2STodor Tomov static struct v4l2_mbus_framefmt * 506ec6859b2STodor Tomov __csid_get_format(struct csid_device *csid, 507ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 508ec6859b2STodor Tomov unsigned int pad, 509ec6859b2STodor Tomov enum v4l2_subdev_format_whence which) 510ec6859b2STodor Tomov { 511ec6859b2STodor Tomov if (which == V4L2_SUBDEV_FORMAT_TRY) 512ec6859b2STodor Tomov return v4l2_subdev_get_try_format(&csid->subdev, cfg, pad); 513ec6859b2STodor Tomov 514ec6859b2STodor Tomov return &csid->fmt[pad]; 515ec6859b2STodor Tomov } 516ec6859b2STodor Tomov 517ec6859b2STodor Tomov /* 518ec6859b2STodor Tomov * csid_try_format - Handle try format by pad subdev method 519ec6859b2STodor Tomov * @csid: CSID device 520ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 521ec6859b2STodor Tomov * @pad: pad on which format is requested 522ec6859b2STodor Tomov * @fmt: pointer to v4l2 format structure 523ec6859b2STodor Tomov * @which: wanted subdev format 524ec6859b2STodor Tomov */ 525ec6859b2STodor Tomov static void csid_try_format(struct csid_device *csid, 526ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 527ec6859b2STodor Tomov unsigned int pad, 528ec6859b2STodor Tomov struct v4l2_mbus_framefmt *fmt, 529ec6859b2STodor Tomov enum v4l2_subdev_format_whence which) 530ec6859b2STodor Tomov { 531ec6859b2STodor Tomov unsigned int i; 532ec6859b2STodor Tomov 533ec6859b2STodor Tomov switch (pad) { 534ec6859b2STodor Tomov case MSM_CSID_PAD_SINK: 535ec6859b2STodor Tomov /* Set format on sink pad */ 536ec6859b2STodor Tomov 537ec6859b2STodor Tomov for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) 538ec6859b2STodor Tomov if (fmt->code == csid_input_fmts[i].code) 539ec6859b2STodor Tomov break; 540ec6859b2STodor Tomov 541ec6859b2STodor Tomov /* If not found, use UYVY as default */ 542ec6859b2STodor Tomov if (i >= ARRAY_SIZE(csid_input_fmts)) 543ec6859b2STodor Tomov fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; 544ec6859b2STodor Tomov 545ec6859b2STodor Tomov fmt->width = clamp_t(u32, fmt->width, 1, 8191); 546ec6859b2STodor Tomov fmt->height = clamp_t(u32, fmt->height, 1, 8191); 547ec6859b2STodor Tomov 548ec6859b2STodor Tomov fmt->field = V4L2_FIELD_NONE; 549ec6859b2STodor Tomov fmt->colorspace = V4L2_COLORSPACE_SRGB; 550ec6859b2STodor Tomov 551ec6859b2STodor Tomov break; 552ec6859b2STodor Tomov 553ec6859b2STodor Tomov case MSM_CSID_PAD_SRC: 554ec6859b2STodor Tomov if (csid->testgen_mode->cur.val == 0) { 555ec6859b2STodor Tomov /* Test generator is disabled, keep pad formats */ 556ec6859b2STodor Tomov /* in sync - set and return a format same as sink pad */ 557ec6859b2STodor Tomov struct v4l2_mbus_framefmt format; 558ec6859b2STodor Tomov 559ec6859b2STodor Tomov format = *__csid_get_format(csid, cfg, 560ec6859b2STodor Tomov MSM_CSID_PAD_SINK, which); 561ec6859b2STodor Tomov *fmt = format; 562ec6859b2STodor Tomov } else { 563ec6859b2STodor Tomov /* Test generator is enabled, set format on source*/ 564ec6859b2STodor Tomov /* pad to allow test generator usage */ 565ec6859b2STodor Tomov 566ec6859b2STodor Tomov for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) 567ec6859b2STodor Tomov if (csid_input_fmts[i].code == fmt->code) 568ec6859b2STodor Tomov break; 569ec6859b2STodor Tomov 570ec6859b2STodor Tomov /* If not found, use UYVY as default */ 571ec6859b2STodor Tomov if (i >= ARRAY_SIZE(csid_input_fmts)) 572ec6859b2STodor Tomov fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; 573ec6859b2STodor Tomov 574ec6859b2STodor Tomov fmt->width = clamp_t(u32, fmt->width, 1, 8191); 575ec6859b2STodor Tomov fmt->height = clamp_t(u32, fmt->height, 1, 8191); 576ec6859b2STodor Tomov 577ec6859b2STodor Tomov fmt->field = V4L2_FIELD_NONE; 578ec6859b2STodor Tomov } 579ec6859b2STodor Tomov break; 580ec6859b2STodor Tomov } 581ec6859b2STodor Tomov 582ec6859b2STodor Tomov fmt->colorspace = V4L2_COLORSPACE_SRGB; 583ec6859b2STodor Tomov } 584ec6859b2STodor Tomov 585ec6859b2STodor Tomov /* 586ec6859b2STodor Tomov * csid_enum_mbus_code - Handle pixel format enumeration 587ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 588ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 589ec6859b2STodor Tomov * @code: pointer to v4l2_subdev_mbus_code_enum structure 590ec6859b2STodor Tomov * return -EINVAL or zero on success 591ec6859b2STodor Tomov */ 592ec6859b2STodor Tomov static int csid_enum_mbus_code(struct v4l2_subdev *sd, 593ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 594ec6859b2STodor Tomov struct v4l2_subdev_mbus_code_enum *code) 595ec6859b2STodor Tomov { 596ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 597ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 598ec6859b2STodor Tomov 599ec6859b2STodor Tomov if (code->pad == MSM_CSID_PAD_SINK) { 600ec6859b2STodor Tomov if (code->index >= ARRAY_SIZE(csid_input_fmts)) 601ec6859b2STodor Tomov return -EINVAL; 602ec6859b2STodor Tomov 603ec6859b2STodor Tomov code->code = csid_input_fmts[code->index].code; 604ec6859b2STodor Tomov } else { 605ec6859b2STodor Tomov if (csid->testgen_mode->cur.val == 0) { 606ec6859b2STodor Tomov if (code->index > 0) 607ec6859b2STodor Tomov return -EINVAL; 608ec6859b2STodor Tomov 609ec6859b2STodor Tomov format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SINK, 610ec6859b2STodor Tomov code->which); 611ec6859b2STodor Tomov 612ec6859b2STodor Tomov code->code = format->code; 613ec6859b2STodor Tomov } else { 614ec6859b2STodor Tomov if (code->index >= ARRAY_SIZE(csid_input_fmts)) 615ec6859b2STodor Tomov return -EINVAL; 616ec6859b2STodor Tomov 617ec6859b2STodor Tomov code->code = csid_input_fmts[code->index].code; 618ec6859b2STodor Tomov } 619ec6859b2STodor Tomov } 620ec6859b2STodor Tomov 621ec6859b2STodor Tomov return 0; 622ec6859b2STodor Tomov } 623ec6859b2STodor Tomov 624ec6859b2STodor Tomov /* 625ec6859b2STodor Tomov * csid_enum_frame_size - Handle frame size enumeration 626ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 627ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 628ec6859b2STodor Tomov * @fse: pointer to v4l2_subdev_frame_size_enum structure 629ec6859b2STodor Tomov * return -EINVAL or zero on success 630ec6859b2STodor Tomov */ 631ec6859b2STodor Tomov static int csid_enum_frame_size(struct v4l2_subdev *sd, 632ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 633ec6859b2STodor Tomov struct v4l2_subdev_frame_size_enum *fse) 634ec6859b2STodor Tomov { 635ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 636ec6859b2STodor Tomov struct v4l2_mbus_framefmt format; 637ec6859b2STodor Tomov 638ec6859b2STodor Tomov if (fse->index != 0) 639ec6859b2STodor Tomov return -EINVAL; 640ec6859b2STodor Tomov 641ec6859b2STodor Tomov format.code = fse->code; 642ec6859b2STodor Tomov format.width = 1; 643ec6859b2STodor Tomov format.height = 1; 644ec6859b2STodor Tomov csid_try_format(csid, cfg, fse->pad, &format, fse->which); 645ec6859b2STodor Tomov fse->min_width = format.width; 646ec6859b2STodor Tomov fse->min_height = format.height; 647ec6859b2STodor Tomov 648ec6859b2STodor Tomov if (format.code != fse->code) 649ec6859b2STodor Tomov return -EINVAL; 650ec6859b2STodor Tomov 651ec6859b2STodor Tomov format.code = fse->code; 652ec6859b2STodor Tomov format.width = -1; 653ec6859b2STodor Tomov format.height = -1; 654ec6859b2STodor Tomov csid_try_format(csid, cfg, fse->pad, &format, fse->which); 655ec6859b2STodor Tomov fse->max_width = format.width; 656ec6859b2STodor Tomov fse->max_height = format.height; 657ec6859b2STodor Tomov 658ec6859b2STodor Tomov return 0; 659ec6859b2STodor Tomov } 660ec6859b2STodor Tomov 661ec6859b2STodor Tomov /* 662ec6859b2STodor Tomov * csid_get_format - Handle get format by pads subdev method 663ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 664ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 665ec6859b2STodor Tomov * @fmt: pointer to v4l2 subdev format structure 666ec6859b2STodor Tomov * 667ec6859b2STodor Tomov * Return -EINVAL or zero on success 668ec6859b2STodor Tomov */ 669ec6859b2STodor Tomov static int csid_get_format(struct v4l2_subdev *sd, 670ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 671ec6859b2STodor Tomov struct v4l2_subdev_format *fmt) 672ec6859b2STodor Tomov { 673ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 674ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 675ec6859b2STodor Tomov 676ec6859b2STodor Tomov format = __csid_get_format(csid, cfg, fmt->pad, fmt->which); 677ec6859b2STodor Tomov if (format == NULL) 678ec6859b2STodor Tomov return -EINVAL; 679ec6859b2STodor Tomov 680ec6859b2STodor Tomov fmt->format = *format; 681ec6859b2STodor Tomov 682ec6859b2STodor Tomov return 0; 683ec6859b2STodor Tomov } 684ec6859b2STodor Tomov 685ec6859b2STodor Tomov /* 686ec6859b2STodor Tomov * csid_set_format - Handle set format by pads subdev method 687ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 688ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 689ec6859b2STodor Tomov * @fmt: pointer to v4l2 subdev format structure 690ec6859b2STodor Tomov * 691ec6859b2STodor Tomov * Return -EINVAL or zero on success 692ec6859b2STodor Tomov */ 693ec6859b2STodor Tomov static int csid_set_format(struct v4l2_subdev *sd, 694ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 695ec6859b2STodor Tomov struct v4l2_subdev_format *fmt) 696ec6859b2STodor Tomov { 697ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 698ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 699ec6859b2STodor Tomov 700ec6859b2STodor Tomov format = __csid_get_format(csid, cfg, fmt->pad, fmt->which); 701ec6859b2STodor Tomov if (format == NULL) 702ec6859b2STodor Tomov return -EINVAL; 703ec6859b2STodor Tomov 704ec6859b2STodor Tomov csid_try_format(csid, cfg, fmt->pad, &fmt->format, fmt->which); 705ec6859b2STodor Tomov *format = fmt->format; 706ec6859b2STodor Tomov 707ec6859b2STodor Tomov /* Propagate the format from sink to source */ 708ec6859b2STodor Tomov if (fmt->pad == MSM_CSID_PAD_SINK) { 709ec6859b2STodor Tomov format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SRC, 710ec6859b2STodor Tomov fmt->which); 711ec6859b2STodor Tomov 712ec6859b2STodor Tomov *format = fmt->format; 713ec6859b2STodor Tomov csid_try_format(csid, cfg, MSM_CSID_PAD_SRC, format, 714ec6859b2STodor Tomov fmt->which); 715ec6859b2STodor Tomov } 716ec6859b2STodor Tomov 717ec6859b2STodor Tomov return 0; 718ec6859b2STodor Tomov } 719ec6859b2STodor Tomov 720ec6859b2STodor Tomov /* 721ec6859b2STodor Tomov * csid_init_formats - Initialize formats on all pads 722ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 723ec6859b2STodor Tomov * @fh: V4L2 subdev file handle 724ec6859b2STodor Tomov * 725ec6859b2STodor Tomov * Initialize all pad formats with default values. 726ec6859b2STodor Tomov * 727ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 728ec6859b2STodor Tomov */ 729ec6859b2STodor Tomov static int csid_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 730ec6859b2STodor Tomov { 731ec6859b2STodor Tomov struct v4l2_subdev_format format = { 732ec6859b2STodor Tomov .pad = MSM_CSID_PAD_SINK, 733ec6859b2STodor Tomov .which = fh ? V4L2_SUBDEV_FORMAT_TRY : 734ec6859b2STodor Tomov V4L2_SUBDEV_FORMAT_ACTIVE, 735ec6859b2STodor Tomov .format = { 736ec6859b2STodor Tomov .code = MEDIA_BUS_FMT_UYVY8_2X8, 737ec6859b2STodor Tomov .width = 1920, 738ec6859b2STodor Tomov .height = 1080 739ec6859b2STodor Tomov } 740ec6859b2STodor Tomov }; 741ec6859b2STodor Tomov 742ec6859b2STodor Tomov return csid_set_format(sd, fh ? fh->pad : NULL, &format); 743ec6859b2STodor Tomov } 744ec6859b2STodor Tomov 745ec6859b2STodor Tomov static const char * const csid_test_pattern_menu[] = { 746ec6859b2STodor Tomov "Disabled", 747ec6859b2STodor Tomov "Incrementing", 748ec6859b2STodor Tomov "Alternating 0x55/0xAA", 749ec6859b2STodor Tomov "All Zeros 0x00", 750ec6859b2STodor Tomov "All Ones 0xFF", 751ec6859b2STodor Tomov "Pseudo-random Data", 752ec6859b2STodor Tomov }; 753ec6859b2STodor Tomov 754ec6859b2STodor Tomov /* 755ec6859b2STodor Tomov * csid_set_test_pattern - Set test generator's pattern mode 756ec6859b2STodor Tomov * @csid: CSID device 757ec6859b2STodor Tomov * @value: desired test pattern mode 758ec6859b2STodor Tomov * 759ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 760ec6859b2STodor Tomov */ 761ec6859b2STodor Tomov static int csid_set_test_pattern(struct csid_device *csid, s32 value) 762ec6859b2STodor Tomov { 763ec6859b2STodor Tomov struct csid_testgen_config *tg = &csid->testgen; 764ec6859b2STodor Tomov 765ec6859b2STodor Tomov /* If CSID is linked to CSIPHY, do not allow to enable test generator */ 766ec6859b2STodor Tomov if (value && media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) 767ec6859b2STodor Tomov return -EBUSY; 768ec6859b2STodor Tomov 769ec6859b2STodor Tomov tg->enabled = !!value; 770ec6859b2STodor Tomov 771ec6859b2STodor Tomov switch (value) { 772ec6859b2STodor Tomov case 1: 773ec6859b2STodor Tomov tg->payload_mode = CSID_PAYLOAD_MODE_INCREMENTING; 774ec6859b2STodor Tomov break; 775ec6859b2STodor Tomov case 2: 776ec6859b2STodor Tomov tg->payload_mode = CSID_PAYLOAD_MODE_ALTERNATING_55_AA; 777ec6859b2STodor Tomov break; 778ec6859b2STodor Tomov case 3: 779ec6859b2STodor Tomov tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ZEROES; 780ec6859b2STodor Tomov break; 781ec6859b2STodor Tomov case 4: 782ec6859b2STodor Tomov tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ONES; 783ec6859b2STodor Tomov break; 784ec6859b2STodor Tomov case 5: 785ec6859b2STodor Tomov tg->payload_mode = CSID_PAYLOAD_MODE_RANDOM; 786ec6859b2STodor Tomov break; 787ec6859b2STodor Tomov } 788ec6859b2STodor Tomov 789ec6859b2STodor Tomov return 0; 790ec6859b2STodor Tomov } 791ec6859b2STodor Tomov 792ec6859b2STodor Tomov /* 793ec6859b2STodor Tomov * csid_s_ctrl - Handle set control subdev method 794ec6859b2STodor Tomov * @ctrl: pointer to v4l2 control structure 795ec6859b2STodor Tomov * 796ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 797ec6859b2STodor Tomov */ 798ec6859b2STodor Tomov static int csid_s_ctrl(struct v4l2_ctrl *ctrl) 799ec6859b2STodor Tomov { 800ec6859b2STodor Tomov struct csid_device *csid = container_of(ctrl->handler, 801ec6859b2STodor Tomov struct csid_device, ctrls); 802ec6859b2STodor Tomov int ret = -EINVAL; 803ec6859b2STodor Tomov 804ec6859b2STodor Tomov switch (ctrl->id) { 805ec6859b2STodor Tomov case V4L2_CID_TEST_PATTERN: 806ec6859b2STodor Tomov ret = csid_set_test_pattern(csid, ctrl->val); 807ec6859b2STodor Tomov break; 808ec6859b2STodor Tomov } 809ec6859b2STodor Tomov 810ec6859b2STodor Tomov return ret; 811ec6859b2STodor Tomov } 812ec6859b2STodor Tomov 813ec6859b2STodor Tomov static const struct v4l2_ctrl_ops csid_ctrl_ops = { 814ec6859b2STodor Tomov .s_ctrl = csid_s_ctrl, 815ec6859b2STodor Tomov }; 816ec6859b2STodor Tomov 817ec6859b2STodor Tomov /* 818ec6859b2STodor Tomov * msm_csid_subdev_init - Initialize CSID device structure and resources 819ec6859b2STodor Tomov * @csid: CSID device 820ec6859b2STodor Tomov * @res: CSID module resources table 821ec6859b2STodor Tomov * @id: CSID module id 822ec6859b2STodor Tomov * 823ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 824ec6859b2STodor Tomov */ 8259c3e59deSTodor Tomov int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, 826ec6859b2STodor Tomov const struct resources *res, u8 id) 827ec6859b2STodor Tomov { 8289c3e59deSTodor Tomov struct device *dev = camss->dev; 829ec6859b2STodor Tomov struct platform_device *pdev = to_platform_device(dev); 830ec6859b2STodor Tomov struct resource *r; 831ec6859b2STodor Tomov int i, j; 832ec6859b2STodor Tomov int ret; 833ec6859b2STodor Tomov 8349c3e59deSTodor Tomov csid->camss = camss; 835ec6859b2STodor Tomov csid->id = id; 836ec6859b2STodor Tomov 837ec6859b2STodor Tomov /* Memory */ 838ec6859b2STodor Tomov 839ec6859b2STodor Tomov r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); 840ec6859b2STodor Tomov csid->base = devm_ioremap_resource(dev, r); 841ec6859b2STodor Tomov if (IS_ERR(csid->base)) { 842ec6859b2STodor Tomov dev_err(dev, "could not map memory\n"); 843ec6859b2STodor Tomov return PTR_ERR(csid->base); 844ec6859b2STodor Tomov } 845ec6859b2STodor Tomov 846ec6859b2STodor Tomov /* Interrupt */ 847ec6859b2STodor Tomov 848ec6859b2STodor Tomov r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, 849ec6859b2STodor Tomov res->interrupt[0]); 850ec6859b2STodor Tomov if (!r) { 851ec6859b2STodor Tomov dev_err(dev, "missing IRQ\n"); 852ec6859b2STodor Tomov return -EINVAL; 853ec6859b2STodor Tomov } 854ec6859b2STodor Tomov 855ec6859b2STodor Tomov csid->irq = r->start; 856ec6859b2STodor Tomov snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d", 857ec6859b2STodor Tomov dev_name(dev), MSM_CSID_NAME, csid->id); 858ec6859b2STodor Tomov ret = devm_request_irq(dev, csid->irq, csid_isr, 859ec6859b2STodor Tomov IRQF_TRIGGER_RISING, csid->irq_name, csid); 860ec6859b2STodor Tomov if (ret < 0) { 861ec6859b2STodor Tomov dev_err(dev, "request_irq failed: %d\n", ret); 862ec6859b2STodor Tomov return ret; 863ec6859b2STodor Tomov } 864ec6859b2STodor Tomov 865ec6859b2STodor Tomov disable_irq(csid->irq); 866ec6859b2STodor Tomov 867ec6859b2STodor Tomov /* Clocks */ 868ec6859b2STodor Tomov 869ec6859b2STodor Tomov csid->nclocks = 0; 870ec6859b2STodor Tomov while (res->clock[csid->nclocks]) 871ec6859b2STodor Tomov csid->nclocks++; 872ec6859b2STodor Tomov 873ec6859b2STodor Tomov csid->clock = devm_kcalloc(dev, csid->nclocks, sizeof(*csid->clock), 874ec6859b2STodor Tomov GFP_KERNEL); 875ec6859b2STodor Tomov if (!csid->clock) 876ec6859b2STodor Tomov return -ENOMEM; 877ec6859b2STodor Tomov 878ec6859b2STodor Tomov for (i = 0; i < csid->nclocks; i++) { 879ec6859b2STodor Tomov struct camss_clock *clock = &csid->clock[i]; 880ec6859b2STodor Tomov 881ec6859b2STodor Tomov clock->clk = devm_clk_get(dev, res->clock[i]); 882ec6859b2STodor Tomov if (IS_ERR(clock->clk)) 883ec6859b2STodor Tomov return PTR_ERR(clock->clk); 884ec6859b2STodor Tomov 885ec6859b2STodor Tomov clock->name = res->clock[i]; 886ec6859b2STodor Tomov 887ec6859b2STodor Tomov clock->nfreqs = 0; 888ec6859b2STodor Tomov while (res->clock_rate[i][clock->nfreqs]) 889ec6859b2STodor Tomov clock->nfreqs++; 890ec6859b2STodor Tomov 891ec6859b2STodor Tomov if (!clock->nfreqs) { 892ec6859b2STodor Tomov clock->freq = NULL; 893ec6859b2STodor Tomov continue; 894ec6859b2STodor Tomov } 895ec6859b2STodor Tomov 896ec6859b2STodor Tomov clock->freq = devm_kcalloc(dev, 897ec6859b2STodor Tomov clock->nfreqs, 898ec6859b2STodor Tomov sizeof(*clock->freq), 899ec6859b2STodor Tomov GFP_KERNEL); 900ec6859b2STodor Tomov if (!clock->freq) 901ec6859b2STodor Tomov return -ENOMEM; 902ec6859b2STodor Tomov 903ec6859b2STodor Tomov for (j = 0; j < clock->nfreqs; j++) 904ec6859b2STodor Tomov clock->freq[j] = res->clock_rate[i][j]; 905ec6859b2STodor Tomov } 906ec6859b2STodor Tomov 907ec6859b2STodor Tomov /* Regulator */ 908ec6859b2STodor Tomov 909ec6859b2STodor Tomov csid->vdda = devm_regulator_get(dev, res->regulator[0]); 910ec6859b2STodor Tomov if (IS_ERR(csid->vdda)) { 911ec6859b2STodor Tomov dev_err(dev, "could not get regulator\n"); 912ec6859b2STodor Tomov return PTR_ERR(csid->vdda); 913ec6859b2STodor Tomov } 914ec6859b2STodor Tomov 915ec6859b2STodor Tomov init_completion(&csid->reset_complete); 916ec6859b2STodor Tomov 917ec6859b2STodor Tomov return 0; 918ec6859b2STodor Tomov } 919ec6859b2STodor Tomov 920ec6859b2STodor Tomov /* 921ec6859b2STodor Tomov * msm_csid_get_csid_id - Get CSID HW module id 922ec6859b2STodor Tomov * @entity: Pointer to CSID media entity structure 923ec6859b2STodor Tomov * @id: Return CSID HW module id here 924ec6859b2STodor Tomov */ 925ec6859b2STodor Tomov void msm_csid_get_csid_id(struct media_entity *entity, u8 *id) 926ec6859b2STodor Tomov { 927ec6859b2STodor Tomov struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); 928ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 929ec6859b2STodor Tomov 930ec6859b2STodor Tomov *id = csid->id; 931ec6859b2STodor Tomov } 932ec6859b2STodor Tomov 933ec6859b2STodor Tomov /* 934ec6859b2STodor Tomov * csid_get_lane_assign - Calculate CSI2 lane assign configuration parameter 935ec6859b2STodor Tomov * @lane_cfg - CSI2 lane configuration 936ec6859b2STodor Tomov * 937ec6859b2STodor Tomov * Return lane assign 938ec6859b2STodor Tomov */ 939ec6859b2STodor Tomov static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg) 940ec6859b2STodor Tomov { 941ec6859b2STodor Tomov u32 lane_assign = 0; 942ec6859b2STodor Tomov int i; 943ec6859b2STodor Tomov 944ec6859b2STodor Tomov for (i = 0; i < lane_cfg->num_data; i++) 945ec6859b2STodor Tomov lane_assign |= lane_cfg->data[i].pos << (i * 4); 946ec6859b2STodor Tomov 947ec6859b2STodor Tomov return lane_assign; 948ec6859b2STodor Tomov } 949ec6859b2STodor Tomov 950ec6859b2STodor Tomov /* 951ec6859b2STodor Tomov * csid_link_setup - Setup CSID connections 952ec6859b2STodor Tomov * @entity: Pointer to media entity structure 953ec6859b2STodor Tomov * @local: Pointer to local pad 954ec6859b2STodor Tomov * @remote: Pointer to remote pad 955ec6859b2STodor Tomov * @flags: Link flags 956ec6859b2STodor Tomov * 957ec6859b2STodor Tomov * Return 0 on success 958ec6859b2STodor Tomov */ 959ec6859b2STodor Tomov static int csid_link_setup(struct media_entity *entity, 960ec6859b2STodor Tomov const struct media_pad *local, 961ec6859b2STodor Tomov const struct media_pad *remote, u32 flags) 962ec6859b2STodor Tomov { 963ec6859b2STodor Tomov if (flags & MEDIA_LNK_FL_ENABLED) 964ec6859b2STodor Tomov if (media_entity_remote_pad(local)) 965ec6859b2STodor Tomov return -EBUSY; 966ec6859b2STodor Tomov 967ec6859b2STodor Tomov if ((local->flags & MEDIA_PAD_FL_SINK) && 968ec6859b2STodor Tomov (flags & MEDIA_LNK_FL_ENABLED)) { 969ec6859b2STodor Tomov struct v4l2_subdev *sd; 970ec6859b2STodor Tomov struct csid_device *csid; 971ec6859b2STodor Tomov struct csiphy_device *csiphy; 972ec6859b2STodor Tomov struct csiphy_lanes_cfg *lane_cfg; 973ec6859b2STodor Tomov struct v4l2_subdev_format format = { 0 }; 974ec6859b2STodor Tomov 975ec6859b2STodor Tomov sd = media_entity_to_v4l2_subdev(entity); 976ec6859b2STodor Tomov csid = v4l2_get_subdevdata(sd); 977ec6859b2STodor Tomov 978ec6859b2STodor Tomov /* If test generator is enabled */ 979ec6859b2STodor Tomov /* do not allow a link from CSIPHY to CSID */ 980ec6859b2STodor Tomov if (csid->testgen_mode->cur.val != 0) 981ec6859b2STodor Tomov return -EBUSY; 982ec6859b2STodor Tomov 983ec6859b2STodor Tomov sd = media_entity_to_v4l2_subdev(remote->entity); 984ec6859b2STodor Tomov csiphy = v4l2_get_subdevdata(sd); 985ec6859b2STodor Tomov 986ec6859b2STodor Tomov /* If a sensor is not linked to CSIPHY */ 987ec6859b2STodor Tomov /* do no allow a link from CSIPHY to CSID */ 988ec6859b2STodor Tomov if (!csiphy->cfg.csi2) 989ec6859b2STodor Tomov return -EPERM; 990ec6859b2STodor Tomov 991ec6859b2STodor Tomov csid->phy.csiphy_id = csiphy->id; 992ec6859b2STodor Tomov 993ec6859b2STodor Tomov lane_cfg = &csiphy->cfg.csi2->lane_cfg; 994ec6859b2STodor Tomov csid->phy.lane_cnt = lane_cfg->num_data; 995ec6859b2STodor Tomov csid->phy.lane_assign = csid_get_lane_assign(lane_cfg); 996ec6859b2STodor Tomov 997ec6859b2STodor Tomov /* Reset format on source pad to sink pad format */ 998ec6859b2STodor Tomov format.pad = MSM_CSID_PAD_SRC; 999ec6859b2STodor Tomov format.which = V4L2_SUBDEV_FORMAT_ACTIVE; 1000ec6859b2STodor Tomov csid_set_format(&csid->subdev, NULL, &format); 1001ec6859b2STodor Tomov } 1002ec6859b2STodor Tomov 1003ec6859b2STodor Tomov return 0; 1004ec6859b2STodor Tomov } 1005ec6859b2STodor Tomov 1006ec6859b2STodor Tomov static const struct v4l2_subdev_core_ops csid_core_ops = { 1007ec6859b2STodor Tomov .s_power = csid_set_power, 1008ec6859b2STodor Tomov }; 1009ec6859b2STodor Tomov 1010ec6859b2STodor Tomov static const struct v4l2_subdev_video_ops csid_video_ops = { 1011ec6859b2STodor Tomov .s_stream = csid_set_stream, 1012ec6859b2STodor Tomov }; 1013ec6859b2STodor Tomov 1014ec6859b2STodor Tomov static const struct v4l2_subdev_pad_ops csid_pad_ops = { 1015ec6859b2STodor Tomov .enum_mbus_code = csid_enum_mbus_code, 1016ec6859b2STodor Tomov .enum_frame_size = csid_enum_frame_size, 1017ec6859b2STodor Tomov .get_fmt = csid_get_format, 1018ec6859b2STodor Tomov .set_fmt = csid_set_format, 1019ec6859b2STodor Tomov }; 1020ec6859b2STodor Tomov 1021ec6859b2STodor Tomov static const struct v4l2_subdev_ops csid_v4l2_ops = { 1022ec6859b2STodor Tomov .core = &csid_core_ops, 1023ec6859b2STodor Tomov .video = &csid_video_ops, 1024ec6859b2STodor Tomov .pad = &csid_pad_ops, 1025ec6859b2STodor Tomov }; 1026ec6859b2STodor Tomov 1027ec6859b2STodor Tomov static const struct v4l2_subdev_internal_ops csid_v4l2_internal_ops = { 1028ec6859b2STodor Tomov .open = csid_init_formats, 1029ec6859b2STodor Tomov }; 1030ec6859b2STodor Tomov 1031ec6859b2STodor Tomov static const struct media_entity_operations csid_media_ops = { 1032ec6859b2STodor Tomov .link_setup = csid_link_setup, 1033ec6859b2STodor Tomov .link_validate = v4l2_subdev_link_validate, 1034ec6859b2STodor Tomov }; 1035ec6859b2STodor Tomov 1036ec6859b2STodor Tomov /* 1037ec6859b2STodor Tomov * msm_csid_register_entity - Register subdev node for CSID module 1038ec6859b2STodor Tomov * @csid: CSID device 1039ec6859b2STodor Tomov * @v4l2_dev: V4L2 device 1040ec6859b2STodor Tomov * 1041ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 1042ec6859b2STodor Tomov */ 1043ec6859b2STodor Tomov int msm_csid_register_entity(struct csid_device *csid, 1044ec6859b2STodor Tomov struct v4l2_device *v4l2_dev) 1045ec6859b2STodor Tomov { 1046ec6859b2STodor Tomov struct v4l2_subdev *sd = &csid->subdev; 1047ec6859b2STodor Tomov struct media_pad *pads = csid->pads; 10489c3e59deSTodor Tomov struct device *dev = csid->camss->dev; 1049ec6859b2STodor Tomov int ret; 1050ec6859b2STodor Tomov 1051ec6859b2STodor Tomov v4l2_subdev_init(sd, &csid_v4l2_ops); 1052ec6859b2STodor Tomov sd->internal_ops = &csid_v4l2_internal_ops; 1053ec6859b2STodor Tomov sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 1054ec6859b2STodor Tomov snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", 1055ec6859b2STodor Tomov MSM_CSID_NAME, csid->id); 1056ec6859b2STodor Tomov v4l2_set_subdevdata(sd, csid); 1057ec6859b2STodor Tomov 1058ec6859b2STodor Tomov ret = v4l2_ctrl_handler_init(&csid->ctrls, 1); 1059ec6859b2STodor Tomov if (ret < 0) { 1060ec6859b2STodor Tomov dev_err(dev, "Failed to init ctrl handler: %d\n", ret); 1061ec6859b2STodor Tomov return ret; 1062ec6859b2STodor Tomov } 1063ec6859b2STodor Tomov 1064ec6859b2STodor Tomov csid->testgen_mode = v4l2_ctrl_new_std_menu_items(&csid->ctrls, 1065ec6859b2STodor Tomov &csid_ctrl_ops, V4L2_CID_TEST_PATTERN, 1066ec6859b2STodor Tomov ARRAY_SIZE(csid_test_pattern_menu) - 1, 0, 0, 1067ec6859b2STodor Tomov csid_test_pattern_menu); 1068ec6859b2STodor Tomov 1069ec6859b2STodor Tomov if (csid->ctrls.error) { 1070ec6859b2STodor Tomov dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error); 1071ec6859b2STodor Tomov ret = csid->ctrls.error; 1072ec6859b2STodor Tomov goto free_ctrl; 1073ec6859b2STodor Tomov } 1074ec6859b2STodor Tomov 1075ec6859b2STodor Tomov csid->subdev.ctrl_handler = &csid->ctrls; 1076ec6859b2STodor Tomov 1077ec6859b2STodor Tomov ret = csid_init_formats(sd, NULL); 1078ec6859b2STodor Tomov if (ret < 0) { 1079ec6859b2STodor Tomov dev_err(dev, "Failed to init format: %d\n", ret); 1080ec6859b2STodor Tomov goto free_ctrl; 1081ec6859b2STodor Tomov } 1082ec6859b2STodor Tomov 1083ec6859b2STodor Tomov pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 1084ec6859b2STodor Tomov pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; 1085ec6859b2STodor Tomov 1086ec6859b2STodor Tomov sd->entity.function = MEDIA_ENT_F_IO_V4L; 1087ec6859b2STodor Tomov sd->entity.ops = &csid_media_ops; 1088ec6859b2STodor Tomov ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads); 1089ec6859b2STodor Tomov if (ret < 0) { 1090ec6859b2STodor Tomov dev_err(dev, "Failed to init media entity: %d\n", ret); 1091ec6859b2STodor Tomov goto free_ctrl; 1092ec6859b2STodor Tomov } 1093ec6859b2STodor Tomov 1094ec6859b2STodor Tomov ret = v4l2_device_register_subdev(v4l2_dev, sd); 1095ec6859b2STodor Tomov if (ret < 0) { 1096ec6859b2STodor Tomov dev_err(dev, "Failed to register subdev: %d\n", ret); 1097ec6859b2STodor Tomov goto media_cleanup; 1098ec6859b2STodor Tomov } 1099ec6859b2STodor Tomov 1100ec6859b2STodor Tomov return 0; 1101ec6859b2STodor Tomov 1102ec6859b2STodor Tomov media_cleanup: 1103ec6859b2STodor Tomov media_entity_cleanup(&sd->entity); 1104ec6859b2STodor Tomov free_ctrl: 1105ec6859b2STodor Tomov v4l2_ctrl_handler_free(&csid->ctrls); 1106ec6859b2STodor Tomov 1107ec6859b2STodor Tomov return ret; 1108ec6859b2STodor Tomov } 1109ec6859b2STodor Tomov 1110ec6859b2STodor Tomov /* 1111ec6859b2STodor Tomov * msm_csid_unregister_entity - Unregister CSID module subdev node 1112ec6859b2STodor Tomov * @csid: CSID device 1113ec6859b2STodor Tomov */ 1114ec6859b2STodor Tomov void msm_csid_unregister_entity(struct csid_device *csid) 1115ec6859b2STodor Tomov { 1116ec6859b2STodor Tomov v4l2_device_unregister_subdev(&csid->subdev); 1117ec6859b2STodor Tomov media_entity_cleanup(&csid->subdev.entity); 1118ec6859b2STodor Tomov v4l2_ctrl_handler_free(&csid->ctrls); 1119ec6859b2STodor Tomov } 1120