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> 13*3799eca5SArnd Bergmann #include <linux/io.h> 14ec6859b2STodor Tomov #include <linux/kernel.h> 15ec6859b2STodor Tomov #include <linux/of.h> 16ec6859b2STodor Tomov #include <linux/platform_device.h> 1702afa816STodor Tomov #include <linux/pm_runtime.h> 18ec6859b2STodor Tomov #include <linux/regulator/consumer.h> 19ec6859b2STodor Tomov #include <media/media-entity.h> 20ec6859b2STodor Tomov #include <media/v4l2-device.h> 21988b3ae3STodor Tomov #include <media/v4l2-event.h> 22ec6859b2STodor Tomov #include <media/v4l2-subdev.h> 23ec6859b2STodor Tomov 24ec6859b2STodor Tomov #include "camss-csid.h" 25ec6859b2STodor Tomov #include "camss.h" 26ec6859b2STodor Tomov 27ec6859b2STodor Tomov #define MSM_CSID_NAME "msm_csid" 28ec6859b2STodor Tomov 29ec6859b2STodor Tomov #define CAMSS_CSID_HW_VERSION 0x0 30ec6859b2STodor Tomov #define CAMSS_CSID_CORE_CTRL_0 0x004 31ec6859b2STodor Tomov #define CAMSS_CSID_CORE_CTRL_1 0x008 322a05493bSTodor Tomov #define CAMSS_CSID_RST_CMD(v) ((v) == CAMSS_8x16 ? 0x00c : 0x010) 332a05493bSTodor Tomov #define CAMSS_CSID_CID_LUT_VC_n(v, n) \ 342a05493bSTodor Tomov (((v) == CAMSS_8x16 ? 0x010 : 0x014) + 0x4 * (n)) 352a05493bSTodor Tomov #define CAMSS_CSID_CID_n_CFG(v, n) \ 362a05493bSTodor Tomov (((v) == CAMSS_8x16 ? 0x020 : 0x024) + 0x4 * (n)) 375019d7c8STodor Tomov #define CAMSS_CSID_CID_n_CFG_ISPIF_EN BIT(0) 385019d7c8STodor Tomov #define CAMSS_CSID_CID_n_CFG_RDI_EN BIT(1) 395019d7c8STodor Tomov #define CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT 4 405019d7c8STodor Tomov #define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_8 (0 << 8) 415019d7c8STodor Tomov #define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16 (1 << 8) 425019d7c8STodor Tomov #define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB (0 << 9) 435019d7c8STodor Tomov #define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_MSB (1 << 9) 445019d7c8STodor Tomov #define CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP (0 << 10) 455019d7c8STodor Tomov #define CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING (1 << 10) 462a05493bSTodor Tomov #define CAMSS_CSID_IRQ_CLEAR_CMD(v) ((v) == CAMSS_8x16 ? 0x060 : 0x064) 472a05493bSTodor Tomov #define CAMSS_CSID_IRQ_MASK(v) ((v) == CAMSS_8x16 ? 0x064 : 0x068) 482a05493bSTodor Tomov #define CAMSS_CSID_IRQ_STATUS(v) ((v) == CAMSS_8x16 ? 0x068 : 0x06c) 492a05493bSTodor Tomov #define CAMSS_CSID_TG_CTRL(v) ((v) == CAMSS_8x16 ? 0x0a0 : 0x0a8) 50ec6859b2STodor Tomov #define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436 51ec6859b2STodor Tomov #define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437 522a05493bSTodor Tomov #define CAMSS_CSID_TG_VC_CFG(v) ((v) == CAMSS_8x16 ? 0x0a4 : 0x0ac) 53ec6859b2STodor Tomov #define CAMSS_CSID_TG_VC_CFG_H_BLANKING 0x3ff 54ec6859b2STodor Tomov #define CAMSS_CSID_TG_VC_CFG_V_BLANKING 0x7f 552a05493bSTodor Tomov #define CAMSS_CSID_TG_DT_n_CGG_0(v, n) \ 562a05493bSTodor Tomov (((v) == CAMSS_8x16 ? 0x0ac : 0x0b4) + 0xc * (n)) 572a05493bSTodor Tomov #define CAMSS_CSID_TG_DT_n_CGG_1(v, n) \ 582a05493bSTodor Tomov (((v) == CAMSS_8x16 ? 0x0b0 : 0x0b8) + 0xc * (n)) 592a05493bSTodor Tomov #define CAMSS_CSID_TG_DT_n_CGG_2(v, n) \ 602a05493bSTodor Tomov (((v) == CAMSS_8x16 ? 0x0b4 : 0x0bc) + 0xc * (n)) 61ec6859b2STodor Tomov 62ec6859b2STodor Tomov #define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12 63ec6859b2STodor Tomov #define DATA_TYPE_YUV422_8BIT 0x1e 64ec6859b2STodor Tomov #define DATA_TYPE_RAW_6BIT 0x28 65ec6859b2STodor Tomov #define DATA_TYPE_RAW_8BIT 0x2a 66ec6859b2STodor Tomov #define DATA_TYPE_RAW_10BIT 0x2b 67ec6859b2STodor Tomov #define DATA_TYPE_RAW_12BIT 0x2c 68f476fb56STodor Tomov #define DATA_TYPE_RAW_14BIT 0x2d 69ec6859b2STodor Tomov 70ec6859b2STodor Tomov #define DECODE_FORMAT_UNCOMPRESSED_6_BIT 0x0 71ec6859b2STodor Tomov #define DECODE_FORMAT_UNCOMPRESSED_8_BIT 0x1 72ec6859b2STodor Tomov #define DECODE_FORMAT_UNCOMPRESSED_10_BIT 0x2 73ec6859b2STodor Tomov #define DECODE_FORMAT_UNCOMPRESSED_12_BIT 0x3 74f476fb56STodor Tomov #define DECODE_FORMAT_UNCOMPRESSED_14_BIT 0x8 75ec6859b2STodor Tomov 76ec6859b2STodor Tomov #define CSID_RESET_TIMEOUT_MS 500 77ec6859b2STodor Tomov 78cba3819dSTodor Tomov struct csid_format { 79ec6859b2STodor Tomov u32 code; 80ec6859b2STodor Tomov u8 data_type; 81ec6859b2STodor Tomov u8 decode_format; 82ec6859b2STodor Tomov u8 bpp; 83ec6859b2STodor Tomov u8 spp; /* bus samples per pixel */ 84ec6859b2STodor Tomov }; 85ec6859b2STodor Tomov 86cba3819dSTodor Tomov static const struct csid_format csid_formats_8x16[] = { 87ec6859b2STodor Tomov { 88ec6859b2STodor Tomov MEDIA_BUS_FMT_UYVY8_2X8, 89ec6859b2STodor Tomov DATA_TYPE_YUV422_8BIT, 90ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 91ec6859b2STodor Tomov 8, 92ec6859b2STodor Tomov 2, 93ec6859b2STodor Tomov }, 94ec6859b2STodor Tomov { 95ec6859b2STodor Tomov MEDIA_BUS_FMT_VYUY8_2X8, 96ec6859b2STodor Tomov DATA_TYPE_YUV422_8BIT, 97ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 98ec6859b2STodor Tomov 8, 99ec6859b2STodor Tomov 2, 100ec6859b2STodor Tomov }, 101ec6859b2STodor Tomov { 102ec6859b2STodor Tomov MEDIA_BUS_FMT_YUYV8_2X8, 103ec6859b2STodor Tomov DATA_TYPE_YUV422_8BIT, 104ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 105ec6859b2STodor Tomov 8, 106ec6859b2STodor Tomov 2, 107ec6859b2STodor Tomov }, 108ec6859b2STodor Tomov { 109ec6859b2STodor Tomov MEDIA_BUS_FMT_YVYU8_2X8, 110ec6859b2STodor Tomov DATA_TYPE_YUV422_8BIT, 111ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 112ec6859b2STodor Tomov 8, 113ec6859b2STodor Tomov 2, 114ec6859b2STodor Tomov }, 115ec6859b2STodor Tomov { 116ec6859b2STodor Tomov MEDIA_BUS_FMT_SBGGR8_1X8, 117ec6859b2STodor Tomov DATA_TYPE_RAW_8BIT, 118ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 119ec6859b2STodor Tomov 8, 120ec6859b2STodor Tomov 1, 121ec6859b2STodor Tomov }, 122ec6859b2STodor Tomov { 123ec6859b2STodor Tomov MEDIA_BUS_FMT_SGBRG8_1X8, 124ec6859b2STodor Tomov DATA_TYPE_RAW_8BIT, 125ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 126ec6859b2STodor Tomov 8, 127ec6859b2STodor Tomov 1, 128ec6859b2STodor Tomov }, 129ec6859b2STodor Tomov { 130ec6859b2STodor Tomov MEDIA_BUS_FMT_SGRBG8_1X8, 131ec6859b2STodor Tomov DATA_TYPE_RAW_8BIT, 132ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 133ec6859b2STodor Tomov 8, 134ec6859b2STodor Tomov 1, 135ec6859b2STodor Tomov }, 136ec6859b2STodor Tomov { 137ec6859b2STodor Tomov MEDIA_BUS_FMT_SRGGB8_1X8, 138ec6859b2STodor Tomov DATA_TYPE_RAW_8BIT, 139ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 140ec6859b2STodor Tomov 8, 141ec6859b2STodor Tomov 1, 142ec6859b2STodor Tomov }, 143ec6859b2STodor Tomov { 144ec6859b2STodor Tomov MEDIA_BUS_FMT_SBGGR10_1X10, 145ec6859b2STodor Tomov DATA_TYPE_RAW_10BIT, 146ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 147ec6859b2STodor Tomov 10, 148ec6859b2STodor Tomov 1, 149ec6859b2STodor Tomov }, 150ec6859b2STodor Tomov { 151ec6859b2STodor Tomov MEDIA_BUS_FMT_SGBRG10_1X10, 152ec6859b2STodor Tomov DATA_TYPE_RAW_10BIT, 153ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 154ec6859b2STodor Tomov 10, 155ec6859b2STodor Tomov 1, 156ec6859b2STodor Tomov }, 157ec6859b2STodor Tomov { 158ec6859b2STodor Tomov MEDIA_BUS_FMT_SGRBG10_1X10, 159ec6859b2STodor Tomov DATA_TYPE_RAW_10BIT, 160ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 161ec6859b2STodor Tomov 10, 162ec6859b2STodor Tomov 1, 163ec6859b2STodor Tomov }, 164ec6859b2STodor Tomov { 165ec6859b2STodor Tomov MEDIA_BUS_FMT_SRGGB10_1X10, 166ec6859b2STodor Tomov DATA_TYPE_RAW_10BIT, 167ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 168ec6859b2STodor Tomov 10, 169ec6859b2STodor Tomov 1, 170ec6859b2STodor Tomov }, 171ec6859b2STodor Tomov { 172ec6859b2STodor Tomov MEDIA_BUS_FMT_SBGGR12_1X12, 173ec6859b2STodor Tomov DATA_TYPE_RAW_12BIT, 174ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_12_BIT, 175ec6859b2STodor Tomov 12, 176ec6859b2STodor Tomov 1, 177ec6859b2STodor Tomov }, 178ec6859b2STodor Tomov { 179ec6859b2STodor Tomov MEDIA_BUS_FMT_SGBRG12_1X12, 180ec6859b2STodor Tomov DATA_TYPE_RAW_12BIT, 181ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_12_BIT, 182ec6859b2STodor Tomov 12, 183ec6859b2STodor Tomov 1, 184ec6859b2STodor Tomov }, 185ec6859b2STodor Tomov { 186ec6859b2STodor Tomov MEDIA_BUS_FMT_SGRBG12_1X12, 187ec6859b2STodor Tomov DATA_TYPE_RAW_12BIT, 188ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_12_BIT, 189ec6859b2STodor Tomov 12, 190ec6859b2STodor Tomov 1, 191ec6859b2STodor Tomov }, 192ec6859b2STodor Tomov { 193ec6859b2STodor Tomov MEDIA_BUS_FMT_SRGGB12_1X12, 194ec6859b2STodor Tomov DATA_TYPE_RAW_12BIT, 195ec6859b2STodor Tomov DECODE_FORMAT_UNCOMPRESSED_12_BIT, 196ec6859b2STodor Tomov 12, 197ec6859b2STodor Tomov 1, 198cc8fe073STodor Tomov }, 199cc8fe073STodor Tomov { 200cc8fe073STodor Tomov MEDIA_BUS_FMT_Y10_1X10, 201cc8fe073STodor Tomov DATA_TYPE_RAW_10BIT, 202cc8fe073STodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 203cc8fe073STodor Tomov 10, 204cc8fe073STodor Tomov 1, 205cc8fe073STodor Tomov }, 206ec6859b2STodor Tomov }; 207ec6859b2STodor Tomov 208cba3819dSTodor Tomov static const struct csid_format csid_formats_8x96[] = { 209cba3819dSTodor Tomov { 210cba3819dSTodor Tomov MEDIA_BUS_FMT_UYVY8_2X8, 211cba3819dSTodor Tomov DATA_TYPE_YUV422_8BIT, 212cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 213cba3819dSTodor Tomov 8, 214cba3819dSTodor Tomov 2, 215cba3819dSTodor Tomov }, 216cba3819dSTodor Tomov { 217cba3819dSTodor Tomov MEDIA_BUS_FMT_VYUY8_2X8, 218cba3819dSTodor Tomov DATA_TYPE_YUV422_8BIT, 219cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 220cba3819dSTodor Tomov 8, 221cba3819dSTodor Tomov 2, 222cba3819dSTodor Tomov }, 223cba3819dSTodor Tomov { 224cba3819dSTodor Tomov MEDIA_BUS_FMT_YUYV8_2X8, 225cba3819dSTodor Tomov DATA_TYPE_YUV422_8BIT, 226cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 227cba3819dSTodor Tomov 8, 228cba3819dSTodor Tomov 2, 229cba3819dSTodor Tomov }, 230cba3819dSTodor Tomov { 231cba3819dSTodor Tomov MEDIA_BUS_FMT_YVYU8_2X8, 232cba3819dSTodor Tomov DATA_TYPE_YUV422_8BIT, 233cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 234cba3819dSTodor Tomov 8, 235cba3819dSTodor Tomov 2, 236cba3819dSTodor Tomov }, 237cba3819dSTodor Tomov { 238cba3819dSTodor Tomov MEDIA_BUS_FMT_SBGGR8_1X8, 239cba3819dSTodor Tomov DATA_TYPE_RAW_8BIT, 240cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 241cba3819dSTodor Tomov 8, 242cba3819dSTodor Tomov 1, 243cba3819dSTodor Tomov }, 244cba3819dSTodor Tomov { 245cba3819dSTodor Tomov MEDIA_BUS_FMT_SGBRG8_1X8, 246cba3819dSTodor Tomov DATA_TYPE_RAW_8BIT, 247cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 248cba3819dSTodor Tomov 8, 249cba3819dSTodor Tomov 1, 250cba3819dSTodor Tomov }, 251cba3819dSTodor Tomov { 252cba3819dSTodor Tomov MEDIA_BUS_FMT_SGRBG8_1X8, 253cba3819dSTodor Tomov DATA_TYPE_RAW_8BIT, 254cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 255cba3819dSTodor Tomov 8, 256cba3819dSTodor Tomov 1, 257cba3819dSTodor Tomov }, 258cba3819dSTodor Tomov { 259cba3819dSTodor Tomov MEDIA_BUS_FMT_SRGGB8_1X8, 260cba3819dSTodor Tomov DATA_TYPE_RAW_8BIT, 261cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_8_BIT, 262cba3819dSTodor Tomov 8, 263cba3819dSTodor Tomov 1, 264cba3819dSTodor Tomov }, 265cba3819dSTodor Tomov { 266cba3819dSTodor Tomov MEDIA_BUS_FMT_SBGGR10_1X10, 267cba3819dSTodor Tomov DATA_TYPE_RAW_10BIT, 268cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 269cba3819dSTodor Tomov 10, 270cba3819dSTodor Tomov 1, 271cba3819dSTodor Tomov }, 272cba3819dSTodor Tomov { 273cba3819dSTodor Tomov MEDIA_BUS_FMT_SGBRG10_1X10, 274cba3819dSTodor Tomov DATA_TYPE_RAW_10BIT, 275cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 276cba3819dSTodor Tomov 10, 277cba3819dSTodor Tomov 1, 278cba3819dSTodor Tomov }, 279cba3819dSTodor Tomov { 280cba3819dSTodor Tomov MEDIA_BUS_FMT_SGRBG10_1X10, 281cba3819dSTodor Tomov DATA_TYPE_RAW_10BIT, 282cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 283cba3819dSTodor Tomov 10, 284cba3819dSTodor Tomov 1, 285cba3819dSTodor Tomov }, 286cba3819dSTodor Tomov { 287cba3819dSTodor Tomov MEDIA_BUS_FMT_SRGGB10_1X10, 288cba3819dSTodor Tomov DATA_TYPE_RAW_10BIT, 289cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 290cba3819dSTodor Tomov 10, 291cba3819dSTodor Tomov 1, 292cba3819dSTodor Tomov }, 293cba3819dSTodor Tomov { 294cba3819dSTodor Tomov MEDIA_BUS_FMT_SBGGR12_1X12, 295cba3819dSTodor Tomov DATA_TYPE_RAW_12BIT, 296cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_12_BIT, 297cba3819dSTodor Tomov 12, 298cba3819dSTodor Tomov 1, 299cba3819dSTodor Tomov }, 300cba3819dSTodor Tomov { 301cba3819dSTodor Tomov MEDIA_BUS_FMT_SGBRG12_1X12, 302cba3819dSTodor Tomov DATA_TYPE_RAW_12BIT, 303cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_12_BIT, 304cba3819dSTodor Tomov 12, 305cba3819dSTodor Tomov 1, 306cba3819dSTodor Tomov }, 307cba3819dSTodor Tomov { 308cba3819dSTodor Tomov MEDIA_BUS_FMT_SGRBG12_1X12, 309cba3819dSTodor Tomov DATA_TYPE_RAW_12BIT, 310cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_12_BIT, 311cba3819dSTodor Tomov 12, 312cba3819dSTodor Tomov 1, 313cba3819dSTodor Tomov }, 314cba3819dSTodor Tomov { 315cba3819dSTodor Tomov MEDIA_BUS_FMT_SRGGB12_1X12, 316cba3819dSTodor Tomov DATA_TYPE_RAW_12BIT, 317cba3819dSTodor Tomov DECODE_FORMAT_UNCOMPRESSED_12_BIT, 318cba3819dSTodor Tomov 12, 319cba3819dSTodor Tomov 1, 320f476fb56STodor Tomov }, 321f476fb56STodor Tomov { 322f476fb56STodor Tomov MEDIA_BUS_FMT_SBGGR14_1X14, 323f476fb56STodor Tomov DATA_TYPE_RAW_14BIT, 324f476fb56STodor Tomov DECODE_FORMAT_UNCOMPRESSED_14_BIT, 325f476fb56STodor Tomov 14, 326f476fb56STodor Tomov 1, 327f476fb56STodor Tomov }, 328f476fb56STodor Tomov { 329f476fb56STodor Tomov MEDIA_BUS_FMT_SGBRG14_1X14, 330f476fb56STodor Tomov DATA_TYPE_RAW_14BIT, 331f476fb56STodor Tomov DECODE_FORMAT_UNCOMPRESSED_14_BIT, 332f476fb56STodor Tomov 14, 333f476fb56STodor Tomov 1, 334f476fb56STodor Tomov }, 335f476fb56STodor Tomov { 336f476fb56STodor Tomov MEDIA_BUS_FMT_SGRBG14_1X14, 337f476fb56STodor Tomov DATA_TYPE_RAW_14BIT, 338f476fb56STodor Tomov DECODE_FORMAT_UNCOMPRESSED_14_BIT, 339f476fb56STodor Tomov 14, 340f476fb56STodor Tomov 1, 341f476fb56STodor Tomov }, 342f476fb56STodor Tomov { 343f476fb56STodor Tomov MEDIA_BUS_FMT_SRGGB14_1X14, 344f476fb56STodor Tomov DATA_TYPE_RAW_14BIT, 345f476fb56STodor Tomov DECODE_FORMAT_UNCOMPRESSED_14_BIT, 346f476fb56STodor Tomov 14, 347f476fb56STodor Tomov 1, 348cc8fe073STodor Tomov }, 349cc8fe073STodor Tomov { 350cc8fe073STodor Tomov MEDIA_BUS_FMT_Y10_1X10, 351cc8fe073STodor Tomov DATA_TYPE_RAW_10BIT, 352cc8fe073STodor Tomov DECODE_FORMAT_UNCOMPRESSED_10_BIT, 353cc8fe073STodor Tomov 10, 354cc8fe073STodor Tomov 1, 355cc8fe073STodor Tomov }, 356cba3819dSTodor Tomov }; 357cba3819dSTodor Tomov 3587e37f47fSTodor Tomov static u32 csid_find_code(u32 *code, unsigned int n_code, 3597e37f47fSTodor Tomov unsigned int index, u32 req_code) 3607e37f47fSTodor Tomov { 3617e37f47fSTodor Tomov int i; 3627e37f47fSTodor Tomov 3637e37f47fSTodor Tomov if (!req_code && (index >= n_code)) 3647e37f47fSTodor Tomov return 0; 3657e37f47fSTodor Tomov 3667e37f47fSTodor Tomov for (i = 0; i < n_code; i++) 3677e37f47fSTodor Tomov if (req_code) { 3687e37f47fSTodor Tomov if (req_code == code[i]) 3697e37f47fSTodor Tomov return req_code; 3707e37f47fSTodor Tomov } else { 3717e37f47fSTodor Tomov if (i == index) 3727e37f47fSTodor Tomov return code[i]; 3737e37f47fSTodor Tomov } 3747e37f47fSTodor Tomov 3757e37f47fSTodor Tomov return code[0]; 3767e37f47fSTodor Tomov } 3777e37f47fSTodor Tomov 3787e37f47fSTodor Tomov static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, 3797e37f47fSTodor Tomov unsigned int index, u32 src_req_code) 3807e37f47fSTodor Tomov { 3817e37f47fSTodor Tomov if (csid->camss->version == CAMSS_8x16) { 3827e37f47fSTodor Tomov if (index > 0) 3837e37f47fSTodor Tomov return 0; 3847e37f47fSTodor Tomov 3857e37f47fSTodor Tomov return sink_code; 3867e37f47fSTodor Tomov } else if (csid->camss->version == CAMSS_8x96) { 3877e37f47fSTodor Tomov switch (sink_code) { 3885019d7c8STodor Tomov case MEDIA_BUS_FMT_SBGGR10_1X10: 3895019d7c8STodor Tomov { 3905019d7c8STodor Tomov u32 src_code[] = { 3915019d7c8STodor Tomov MEDIA_BUS_FMT_SBGGR10_1X10, 3925019d7c8STodor Tomov MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 3935019d7c8STodor Tomov }; 3945019d7c8STodor Tomov 3955019d7c8STodor Tomov return csid_find_code(src_code, ARRAY_SIZE(src_code), 3965019d7c8STodor Tomov index, src_req_code); 3975019d7c8STodor Tomov } 398cc8fe073STodor Tomov case MEDIA_BUS_FMT_Y10_1X10: 399cc8fe073STodor Tomov { 400cc8fe073STodor Tomov u32 src_code[] = { 401cc8fe073STodor Tomov MEDIA_BUS_FMT_Y10_1X10, 402cc8fe073STodor Tomov MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 403cc8fe073STodor Tomov }; 404cc8fe073STodor Tomov 405cc8fe073STodor Tomov return csid_find_code(src_code, ARRAY_SIZE(src_code), 406cc8fe073STodor Tomov index, src_req_code); 407cc8fe073STodor Tomov } 4087e37f47fSTodor Tomov default: 4097e37f47fSTodor Tomov if (index > 0) 4107e37f47fSTodor Tomov return 0; 4117e37f47fSTodor Tomov 4127e37f47fSTodor Tomov return sink_code; 4137e37f47fSTodor Tomov } 4147e37f47fSTodor Tomov } else { 4157e37f47fSTodor Tomov return 0; 4167e37f47fSTodor Tomov } 4177e37f47fSTodor Tomov } 4187e37f47fSTodor Tomov 419cba3819dSTodor Tomov static const struct csid_format *csid_get_fmt_entry( 420cba3819dSTodor Tomov const struct csid_format *formats, 421cba3819dSTodor Tomov unsigned int nformat, 422cba3819dSTodor Tomov u32 code) 423ec6859b2STodor Tomov { 424ec6859b2STodor Tomov unsigned int i; 425ec6859b2STodor Tomov 426cba3819dSTodor Tomov for (i = 0; i < nformat; i++) 427cba3819dSTodor Tomov if (code == formats[i].code) 428cba3819dSTodor Tomov return &formats[i]; 429ec6859b2STodor Tomov 430ec6859b2STodor Tomov WARN(1, "Unknown format\n"); 431ec6859b2STodor Tomov 432cba3819dSTodor Tomov return &formats[0]; 433ec6859b2STodor Tomov } 434ec6859b2STodor Tomov 435ec6859b2STodor Tomov /* 436ec6859b2STodor Tomov * csid_isr - CSID module interrupt handler 437ec6859b2STodor Tomov * @irq: Interrupt line 438ec6859b2STodor Tomov * @dev: CSID device 439ec6859b2STodor Tomov * 440ec6859b2STodor Tomov * Return IRQ_HANDLED on success 441ec6859b2STodor Tomov */ 442ec6859b2STodor Tomov static irqreturn_t csid_isr(int irq, void *dev) 443ec6859b2STodor Tomov { 444ec6859b2STodor Tomov struct csid_device *csid = dev; 4452a05493bSTodor Tomov enum camss_version ver = csid->camss->version; 446ec6859b2STodor Tomov u32 value; 447ec6859b2STodor Tomov 4482a05493bSTodor Tomov value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS(ver)); 4492a05493bSTodor Tomov writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD(ver)); 450ec6859b2STodor Tomov 451ec6859b2STodor Tomov if ((value >> 11) & 0x1) 452ec6859b2STodor Tomov complete(&csid->reset_complete); 453ec6859b2STodor Tomov 454ec6859b2STodor Tomov return IRQ_HANDLED; 455ec6859b2STodor Tomov } 456ec6859b2STodor Tomov 457ec6859b2STodor Tomov /* 458ec6859b2STodor Tomov * csid_set_clock_rates - Calculate and set clock rates on CSID module 459ec6859b2STodor Tomov * @csiphy: CSID device 460ec6859b2STodor Tomov */ 461ec6859b2STodor Tomov static int csid_set_clock_rates(struct csid_device *csid) 462ec6859b2STodor Tomov { 4639c3e59deSTodor Tomov struct device *dev = csid->camss->dev; 464ec6859b2STodor Tomov u32 pixel_clock; 465ec6859b2STodor Tomov int i, j; 466ec6859b2STodor Tomov int ret; 467ec6859b2STodor Tomov 468ec6859b2STodor Tomov ret = camss_get_pixel_clock(&csid->subdev.entity, &pixel_clock); 469ec6859b2STodor Tomov if (ret) 470ec6859b2STodor Tomov pixel_clock = 0; 471ec6859b2STodor Tomov 472ec6859b2STodor Tomov for (i = 0; i < csid->nclocks; i++) { 473ec6859b2STodor Tomov struct camss_clock *clock = &csid->clock[i]; 474ec6859b2STodor Tomov 475ec6859b2STodor Tomov if (!strcmp(clock->name, "csi0") || 4769c3e59deSTodor Tomov !strcmp(clock->name, "csi1") || 4779c3e59deSTodor Tomov !strcmp(clock->name, "csi2") || 4789c3e59deSTodor Tomov !strcmp(clock->name, "csi3")) { 479cba3819dSTodor Tomov const struct csid_format *f = csid_get_fmt_entry( 480cba3819dSTodor Tomov csid->formats, 481cba3819dSTodor Tomov csid->nformats, 482cba3819dSTodor Tomov csid->fmt[MSM_CSIPHY_PAD_SINK].code); 483ec6859b2STodor Tomov u8 num_lanes = csid->phy.lane_cnt; 484cba3819dSTodor Tomov u64 min_rate = pixel_clock * f->bpp / 485cba3819dSTodor Tomov (2 * num_lanes * 4); 486ec6859b2STodor Tomov long rate; 487ec6859b2STodor Tomov 488ec6859b2STodor Tomov camss_add_clock_margin(&min_rate); 489ec6859b2STodor Tomov 490ec6859b2STodor Tomov for (j = 0; j < clock->nfreqs; j++) 491ec6859b2STodor Tomov if (min_rate < clock->freq[j]) 492ec6859b2STodor Tomov break; 493ec6859b2STodor Tomov 494ec6859b2STodor Tomov if (j == clock->nfreqs) { 495ec6859b2STodor Tomov dev_err(dev, 496ec6859b2STodor Tomov "Pixel clock is too high for CSID\n"); 497ec6859b2STodor Tomov return -EINVAL; 498ec6859b2STodor Tomov } 499ec6859b2STodor Tomov 500ec6859b2STodor Tomov /* if sensor pixel clock is not available */ 501ec6859b2STodor Tomov /* set highest possible CSID clock rate */ 502ec6859b2STodor Tomov if (min_rate == 0) 503ec6859b2STodor Tomov j = clock->nfreqs - 1; 504ec6859b2STodor Tomov 505ec6859b2STodor Tomov rate = clk_round_rate(clock->clk, clock->freq[j]); 506ec6859b2STodor Tomov if (rate < 0) { 507ec6859b2STodor Tomov dev_err(dev, "clk round rate failed: %ld\n", 508ec6859b2STodor Tomov rate); 509ec6859b2STodor Tomov return -EINVAL; 510ec6859b2STodor Tomov } 511ec6859b2STodor Tomov 512ec6859b2STodor Tomov ret = clk_set_rate(clock->clk, rate); 513ec6859b2STodor Tomov if (ret < 0) { 514ec6859b2STodor Tomov dev_err(dev, "clk set rate failed: %d\n", ret); 515ec6859b2STodor Tomov return ret; 516ec6859b2STodor Tomov } 517ec6859b2STodor Tomov } 518ec6859b2STodor Tomov } 519ec6859b2STodor Tomov 520ec6859b2STodor Tomov return 0; 521ec6859b2STodor Tomov } 522ec6859b2STodor Tomov 523ec6859b2STodor Tomov /* 524ec6859b2STodor Tomov * csid_reset - Trigger reset on CSID module and wait to complete 525ec6859b2STodor Tomov * @csid: CSID device 526ec6859b2STodor Tomov * 527ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 528ec6859b2STodor Tomov */ 529ec6859b2STodor Tomov static int csid_reset(struct csid_device *csid) 530ec6859b2STodor Tomov { 531ec6859b2STodor Tomov unsigned long time; 532ec6859b2STodor Tomov 533ec6859b2STodor Tomov reinit_completion(&csid->reset_complete); 534ec6859b2STodor Tomov 5352a05493bSTodor Tomov writel_relaxed(0x7fff, csid->base + 5362a05493bSTodor Tomov CAMSS_CSID_RST_CMD(csid->camss->version)); 537ec6859b2STodor Tomov 538ec6859b2STodor Tomov time = wait_for_completion_timeout(&csid->reset_complete, 539ec6859b2STodor Tomov msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); 540ec6859b2STodor Tomov if (!time) { 5419c3e59deSTodor Tomov dev_err(csid->camss->dev, "CSID reset timeout\n"); 542ec6859b2STodor Tomov return -EIO; 543ec6859b2STodor Tomov } 544ec6859b2STodor Tomov 545ec6859b2STodor Tomov return 0; 546ec6859b2STodor Tomov } 547ec6859b2STodor Tomov 548ec6859b2STodor Tomov /* 549ec6859b2STodor Tomov * csid_set_power - Power on/off CSID module 550ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 551ec6859b2STodor Tomov * @on: Requested power state 552ec6859b2STodor Tomov * 553ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 554ec6859b2STodor Tomov */ 555ec6859b2STodor Tomov static int csid_set_power(struct v4l2_subdev *sd, int on) 556ec6859b2STodor Tomov { 557ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 5589c3e59deSTodor Tomov struct device *dev = csid->camss->dev; 559ec6859b2STodor Tomov int ret; 560ec6859b2STodor Tomov 561ec6859b2STodor Tomov if (on) { 562ec6859b2STodor Tomov u32 hw_version; 563ec6859b2STodor Tomov 56402afa816STodor Tomov ret = pm_runtime_get_sync(dev); 565ec6859b2STodor Tomov if (ret < 0) 566ec6859b2STodor Tomov return ret; 567ec6859b2STodor Tomov 56802afa816STodor Tomov ret = regulator_enable(csid->vdda); 56902afa816STodor Tomov if (ret < 0) { 57002afa816STodor Tomov pm_runtime_put_sync(dev); 57102afa816STodor Tomov return ret; 57202afa816STodor Tomov } 57302afa816STodor Tomov 574ec6859b2STodor Tomov ret = csid_set_clock_rates(csid); 575ec6859b2STodor Tomov if (ret < 0) { 576ec6859b2STodor Tomov regulator_disable(csid->vdda); 57702afa816STodor Tomov pm_runtime_put_sync(dev); 578ec6859b2STodor Tomov return ret; 579ec6859b2STodor Tomov } 580ec6859b2STodor Tomov 581ec6859b2STodor Tomov ret = camss_enable_clocks(csid->nclocks, csid->clock, dev); 582ec6859b2STodor Tomov if (ret < 0) { 583ec6859b2STodor Tomov regulator_disable(csid->vdda); 58402afa816STodor Tomov pm_runtime_put_sync(dev); 585ec6859b2STodor Tomov return ret; 586ec6859b2STodor Tomov } 587ec6859b2STodor Tomov 588ec6859b2STodor Tomov enable_irq(csid->irq); 589ec6859b2STodor Tomov 590ec6859b2STodor Tomov ret = csid_reset(csid); 591ec6859b2STodor Tomov if (ret < 0) { 592ec6859b2STodor Tomov disable_irq(csid->irq); 593ec6859b2STodor Tomov camss_disable_clocks(csid->nclocks, csid->clock); 594ec6859b2STodor Tomov regulator_disable(csid->vdda); 59502afa816STodor Tomov pm_runtime_put_sync(dev); 596ec6859b2STodor Tomov return ret; 597ec6859b2STodor Tomov } 598ec6859b2STodor Tomov 599ec6859b2STodor Tomov hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION); 600ec6859b2STodor Tomov dev_dbg(dev, "CSID HW Version = 0x%08x\n", hw_version); 601ec6859b2STodor Tomov } else { 602ec6859b2STodor Tomov disable_irq(csid->irq); 603ec6859b2STodor Tomov camss_disable_clocks(csid->nclocks, csid->clock); 604ec6859b2STodor Tomov ret = regulator_disable(csid->vdda); 60502afa816STodor Tomov pm_runtime_put_sync(dev); 606ec6859b2STodor Tomov } 607ec6859b2STodor Tomov 608ec6859b2STodor Tomov return ret; 609ec6859b2STodor Tomov } 610ec6859b2STodor Tomov 611ec6859b2STodor Tomov /* 612ec6859b2STodor Tomov * csid_set_stream - Enable/disable streaming on CSID module 613ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 614ec6859b2STodor Tomov * @enable: Requested streaming state 615ec6859b2STodor Tomov * 616ec6859b2STodor Tomov * Main configuration of CSID module is also done here. 617ec6859b2STodor Tomov * 618ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 619ec6859b2STodor Tomov */ 620ec6859b2STodor Tomov static int csid_set_stream(struct v4l2_subdev *sd, int enable) 621ec6859b2STodor Tomov { 622ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 623ec6859b2STodor Tomov struct csid_testgen_config *tg = &csid->testgen; 6242a05493bSTodor Tomov enum camss_version ver = csid->camss->version; 625ec6859b2STodor Tomov u32 val; 626ec6859b2STodor Tomov 627ec6859b2STodor Tomov if (enable) { 628ec6859b2STodor Tomov u8 vc = 0; /* Virtual Channel 0 */ 629ec6859b2STodor Tomov u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ 630ec6859b2STodor Tomov u8 dt, dt_shift, df; 631ec6859b2STodor Tomov int ret; 632ec6859b2STodor Tomov 633ec6859b2STodor Tomov ret = v4l2_ctrl_handler_setup(&csid->ctrls); 634ec6859b2STodor Tomov if (ret < 0) { 6359c3e59deSTodor Tomov dev_err(csid->camss->dev, 636ec6859b2STodor Tomov "could not sync v4l2 controls: %d\n", ret); 637ec6859b2STodor Tomov return ret; 638ec6859b2STodor Tomov } 639ec6859b2STodor Tomov 640ec6859b2STodor Tomov if (!tg->enabled && 641ec6859b2STodor Tomov !media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) 642ec6859b2STodor Tomov return -ENOLINK; 643ec6859b2STodor Tomov 644ec6859b2STodor Tomov if (tg->enabled) { 645ec6859b2STodor Tomov /* Config Test Generator */ 646ec6859b2STodor Tomov struct v4l2_mbus_framefmt *f = 647ec6859b2STodor Tomov &csid->fmt[MSM_CSID_PAD_SRC]; 648cba3819dSTodor Tomov const struct csid_format *format = csid_get_fmt_entry( 649cba3819dSTodor Tomov csid->formats, csid->nformats, f->code); 650cba3819dSTodor Tomov u32 num_bytes_per_line = 651cba3819dSTodor Tomov f->width * format->bpp * format->spp / 8; 652ec6859b2STodor Tomov u32 num_lines = f->height; 653ec6859b2STodor Tomov 654ec6859b2STodor Tomov /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */ 655ec6859b2STodor Tomov /* 1:0 VC */ 656ec6859b2STodor Tomov val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) | 657ec6859b2STodor Tomov ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13); 6582a05493bSTodor Tomov writel_relaxed(val, csid->base + 6592a05493bSTodor Tomov CAMSS_CSID_TG_VC_CFG(ver)); 660ec6859b2STodor Tomov 661ec6859b2STodor Tomov /* 28:16 bytes per lines, 12:0 num of lines */ 662ec6859b2STodor Tomov val = ((num_bytes_per_line & 0x1fff) << 16) | 663ec6859b2STodor Tomov (num_lines & 0x1fff); 664ec6859b2STodor Tomov writel_relaxed(val, csid->base + 6652a05493bSTodor Tomov CAMSS_CSID_TG_DT_n_CGG_0(ver, 0)); 666ec6859b2STodor Tomov 667cba3819dSTodor Tomov dt = format->data_type; 668c628e788STodor Tomov 669ec6859b2STodor Tomov /* 5:0 data type */ 670ec6859b2STodor Tomov val = dt; 671ec6859b2STodor Tomov writel_relaxed(val, csid->base + 6722a05493bSTodor Tomov CAMSS_CSID_TG_DT_n_CGG_1(ver, 0)); 673ec6859b2STodor Tomov 674ec6859b2STodor Tomov /* 2:0 output test pattern */ 675ec6859b2STodor Tomov val = tg->payload_mode; 676ec6859b2STodor Tomov writel_relaxed(val, csid->base + 6772a05493bSTodor Tomov CAMSS_CSID_TG_DT_n_CGG_2(ver, 0)); 678c628e788STodor Tomov 679cba3819dSTodor Tomov df = format->decode_format; 680ec6859b2STodor Tomov } else { 681cba3819dSTodor Tomov struct v4l2_mbus_framefmt *f = 682cba3819dSTodor Tomov &csid->fmt[MSM_CSID_PAD_SINK]; 683cba3819dSTodor Tomov const struct csid_format *format = csid_get_fmt_entry( 684cba3819dSTodor Tomov csid->formats, csid->nformats, f->code); 685ec6859b2STodor Tomov struct csid_phy_config *phy = &csid->phy; 686ec6859b2STodor Tomov 687ec6859b2STodor Tomov val = phy->lane_cnt - 1; 688ec6859b2STodor Tomov val |= phy->lane_assign << 4; 689ec6859b2STodor Tomov 690ec6859b2STodor Tomov writel_relaxed(val, 691ec6859b2STodor Tomov csid->base + CAMSS_CSID_CORE_CTRL_0); 692ec6859b2STodor Tomov 693ec6859b2STodor Tomov val = phy->csiphy_id << 17; 694ec6859b2STodor Tomov val |= 0x9; 695ec6859b2STodor Tomov 696ec6859b2STodor Tomov writel_relaxed(val, 697ec6859b2STodor Tomov csid->base + CAMSS_CSID_CORE_CTRL_1); 698c628e788STodor Tomov 699cba3819dSTodor Tomov dt = format->data_type; 700cba3819dSTodor Tomov df = format->decode_format; 701ec6859b2STodor Tomov } 702ec6859b2STodor Tomov 703ec6859b2STodor Tomov /* Config LUT */ 704ec6859b2STodor Tomov 705ec6859b2STodor Tomov dt_shift = (cid % 4) * 8; 706ec6859b2STodor Tomov 7072a05493bSTodor Tomov val = readl_relaxed(csid->base + 7082a05493bSTodor Tomov CAMSS_CSID_CID_LUT_VC_n(ver, vc)); 709ec6859b2STodor Tomov val &= ~(0xff << dt_shift); 710ec6859b2STodor Tomov val |= dt << dt_shift; 7112a05493bSTodor Tomov writel_relaxed(val, csid->base + 7122a05493bSTodor Tomov CAMSS_CSID_CID_LUT_VC_n(ver, vc)); 713ec6859b2STodor Tomov 7145019d7c8STodor Tomov val = CAMSS_CSID_CID_n_CFG_ISPIF_EN; 7155019d7c8STodor Tomov val |= CAMSS_CSID_CID_n_CFG_RDI_EN; 7165019d7c8STodor Tomov val |= df << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT; 7175019d7c8STodor Tomov val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP; 718cc8fe073STodor Tomov 719cc8fe073STodor Tomov if (csid->camss->version == CAMSS_8x96) { 720cc8fe073STodor Tomov u32 sink_code = csid->fmt[MSM_CSID_PAD_SINK].code; 721cc8fe073STodor Tomov u32 src_code = csid->fmt[MSM_CSID_PAD_SRC].code; 722cc8fe073STodor Tomov 723cc8fe073STodor Tomov if ((sink_code == MEDIA_BUS_FMT_SBGGR10_1X10 && 724cc8fe073STodor Tomov src_code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) || 725cc8fe073STodor Tomov (sink_code == MEDIA_BUS_FMT_Y10_1X10 && 726cc8fe073STodor Tomov src_code == MEDIA_BUS_FMT_Y10_2X8_PADHI_LE)) { 7275019d7c8STodor Tomov val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING; 7285019d7c8STodor Tomov val |= CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16; 7295019d7c8STodor Tomov val |= CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB; 7305019d7c8STodor Tomov } 731cc8fe073STodor Tomov } 732cc8fe073STodor Tomov 7332a05493bSTodor Tomov writel_relaxed(val, csid->base + 7342a05493bSTodor Tomov CAMSS_CSID_CID_n_CFG(ver, cid)); 735ec6859b2STodor Tomov 736ec6859b2STodor Tomov if (tg->enabled) { 737ec6859b2STodor Tomov val = CAMSS_CSID_TG_CTRL_ENABLE; 7382a05493bSTodor Tomov writel_relaxed(val, csid->base + 7392a05493bSTodor Tomov CAMSS_CSID_TG_CTRL(ver)); 740ec6859b2STodor Tomov } 741ec6859b2STodor Tomov } else { 742ec6859b2STodor Tomov if (tg->enabled) { 743ec6859b2STodor Tomov val = CAMSS_CSID_TG_CTRL_DISABLE; 7442a05493bSTodor Tomov writel_relaxed(val, csid->base + 7452a05493bSTodor Tomov CAMSS_CSID_TG_CTRL(ver)); 746ec6859b2STodor Tomov } 747ec6859b2STodor Tomov } 748ec6859b2STodor Tomov 749ec6859b2STodor Tomov return 0; 750ec6859b2STodor Tomov } 751ec6859b2STodor Tomov 752ec6859b2STodor Tomov /* 753ec6859b2STodor Tomov * __csid_get_format - Get pointer to format structure 754ec6859b2STodor Tomov * @csid: CSID device 755ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 756ec6859b2STodor Tomov * @pad: pad from which format is requested 757ec6859b2STodor Tomov * @which: TRY or ACTIVE format 758ec6859b2STodor Tomov * 759ec6859b2STodor Tomov * Return pointer to TRY or ACTIVE format structure 760ec6859b2STodor Tomov */ 761ec6859b2STodor Tomov static struct v4l2_mbus_framefmt * 762ec6859b2STodor Tomov __csid_get_format(struct csid_device *csid, 763ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 764ec6859b2STodor Tomov unsigned int pad, 765ec6859b2STodor Tomov enum v4l2_subdev_format_whence which) 766ec6859b2STodor Tomov { 767ec6859b2STodor Tomov if (which == V4L2_SUBDEV_FORMAT_TRY) 768ec6859b2STodor Tomov return v4l2_subdev_get_try_format(&csid->subdev, cfg, pad); 769ec6859b2STodor Tomov 770ec6859b2STodor Tomov return &csid->fmt[pad]; 771ec6859b2STodor Tomov } 772ec6859b2STodor Tomov 773ec6859b2STodor Tomov /* 774ec6859b2STodor Tomov * csid_try_format - Handle try format by pad subdev method 775ec6859b2STodor Tomov * @csid: CSID device 776ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 777ec6859b2STodor Tomov * @pad: pad on which format is requested 778ec6859b2STodor Tomov * @fmt: pointer to v4l2 format structure 779ec6859b2STodor Tomov * @which: wanted subdev format 780ec6859b2STodor Tomov */ 781ec6859b2STodor Tomov static void csid_try_format(struct csid_device *csid, 782ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 783ec6859b2STodor Tomov unsigned int pad, 784ec6859b2STodor Tomov struct v4l2_mbus_framefmt *fmt, 785ec6859b2STodor Tomov enum v4l2_subdev_format_whence which) 786ec6859b2STodor Tomov { 787ec6859b2STodor Tomov unsigned int i; 788ec6859b2STodor Tomov 789ec6859b2STodor Tomov switch (pad) { 790ec6859b2STodor Tomov case MSM_CSID_PAD_SINK: 791ec6859b2STodor Tomov /* Set format on sink pad */ 792ec6859b2STodor Tomov 793cba3819dSTodor Tomov for (i = 0; i < csid->nformats; i++) 794cba3819dSTodor Tomov if (fmt->code == csid->formats[i].code) 795ec6859b2STodor Tomov break; 796ec6859b2STodor Tomov 797ec6859b2STodor Tomov /* If not found, use UYVY as default */ 798cba3819dSTodor Tomov if (i >= csid->nformats) 799ec6859b2STodor Tomov fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; 800ec6859b2STodor Tomov 801ec6859b2STodor Tomov fmt->width = clamp_t(u32, fmt->width, 1, 8191); 802ec6859b2STodor Tomov fmt->height = clamp_t(u32, fmt->height, 1, 8191); 803ec6859b2STodor Tomov 804ec6859b2STodor Tomov fmt->field = V4L2_FIELD_NONE; 805ec6859b2STodor Tomov fmt->colorspace = V4L2_COLORSPACE_SRGB; 806ec6859b2STodor Tomov 807ec6859b2STodor Tomov break; 808ec6859b2STodor Tomov 809ec6859b2STodor Tomov case MSM_CSID_PAD_SRC: 810ec6859b2STodor Tomov if (csid->testgen_mode->cur.val == 0) { 8117e37f47fSTodor Tomov /* Test generator is disabled, */ 8127e37f47fSTodor Tomov /* keep pad formats in sync */ 8137e37f47fSTodor Tomov u32 code = fmt->code; 814ec6859b2STodor Tomov 8157e37f47fSTodor Tomov *fmt = *__csid_get_format(csid, cfg, 816ec6859b2STodor Tomov MSM_CSID_PAD_SINK, which); 8177e37f47fSTodor Tomov fmt->code = csid_src_pad_code(csid, fmt->code, 0, code); 818ec6859b2STodor Tomov } else { 819ec6859b2STodor Tomov /* Test generator is enabled, set format on source */ 820ec6859b2STodor Tomov /* pad to allow test generator usage */ 821ec6859b2STodor Tomov 822cba3819dSTodor Tomov for (i = 0; i < csid->nformats; i++) 823cba3819dSTodor Tomov if (csid->formats[i].code == fmt->code) 824ec6859b2STodor Tomov break; 825ec6859b2STodor Tomov 826ec6859b2STodor Tomov /* If not found, use UYVY as default */ 827cba3819dSTodor Tomov if (i >= csid->nformats) 828ec6859b2STodor Tomov fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; 829ec6859b2STodor Tomov 830ec6859b2STodor Tomov fmt->width = clamp_t(u32, fmt->width, 1, 8191); 831ec6859b2STodor Tomov fmt->height = clamp_t(u32, fmt->height, 1, 8191); 832ec6859b2STodor Tomov 833ec6859b2STodor Tomov fmt->field = V4L2_FIELD_NONE; 834ec6859b2STodor Tomov } 835ec6859b2STodor Tomov break; 836ec6859b2STodor Tomov } 837ec6859b2STodor Tomov 838ec6859b2STodor Tomov fmt->colorspace = V4L2_COLORSPACE_SRGB; 839ec6859b2STodor Tomov } 840ec6859b2STodor Tomov 841ec6859b2STodor Tomov /* 842ec6859b2STodor Tomov * csid_enum_mbus_code - Handle pixel format enumeration 843ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 844ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 845ec6859b2STodor Tomov * @code: pointer to v4l2_subdev_mbus_code_enum structure 846ec6859b2STodor Tomov * return -EINVAL or zero on success 847ec6859b2STodor Tomov */ 848ec6859b2STodor Tomov static int csid_enum_mbus_code(struct v4l2_subdev *sd, 849ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 850ec6859b2STodor Tomov struct v4l2_subdev_mbus_code_enum *code) 851ec6859b2STodor Tomov { 852ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 853ec6859b2STodor Tomov 854ec6859b2STodor Tomov if (code->pad == MSM_CSID_PAD_SINK) { 855cba3819dSTodor Tomov if (code->index >= csid->nformats) 856ec6859b2STodor Tomov return -EINVAL; 857ec6859b2STodor Tomov 858cba3819dSTodor Tomov code->code = csid->formats[code->index].code; 859ec6859b2STodor Tomov } else { 860ec6859b2STodor Tomov if (csid->testgen_mode->cur.val == 0) { 8617e37f47fSTodor Tomov struct v4l2_mbus_framefmt *sink_fmt; 862ec6859b2STodor Tomov 8637e37f47fSTodor Tomov sink_fmt = __csid_get_format(csid, cfg, 8647e37f47fSTodor Tomov MSM_CSID_PAD_SINK, 865ec6859b2STodor Tomov code->which); 866ec6859b2STodor Tomov 8677e37f47fSTodor Tomov code->code = csid_src_pad_code(csid, sink_fmt->code, 8687e37f47fSTodor Tomov code->index, 0); 8697e37f47fSTodor Tomov if (!code->code) 8707e37f47fSTodor Tomov return -EINVAL; 871ec6859b2STodor Tomov } else { 872cba3819dSTodor Tomov if (code->index >= csid->nformats) 873ec6859b2STodor Tomov return -EINVAL; 874ec6859b2STodor Tomov 875cba3819dSTodor Tomov code->code = csid->formats[code->index].code; 876ec6859b2STodor Tomov } 877ec6859b2STodor Tomov } 878ec6859b2STodor Tomov 879ec6859b2STodor Tomov return 0; 880ec6859b2STodor Tomov } 881ec6859b2STodor Tomov 882ec6859b2STodor Tomov /* 883ec6859b2STodor Tomov * csid_enum_frame_size - Handle frame size enumeration 884ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 885ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 886ec6859b2STodor Tomov * @fse: pointer to v4l2_subdev_frame_size_enum structure 887ec6859b2STodor Tomov * return -EINVAL or zero on success 888ec6859b2STodor Tomov */ 889ec6859b2STodor Tomov static int csid_enum_frame_size(struct v4l2_subdev *sd, 890ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 891ec6859b2STodor Tomov struct v4l2_subdev_frame_size_enum *fse) 892ec6859b2STodor Tomov { 893ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 894ec6859b2STodor Tomov struct v4l2_mbus_framefmt format; 895ec6859b2STodor Tomov 896ec6859b2STodor Tomov if (fse->index != 0) 897ec6859b2STodor Tomov return -EINVAL; 898ec6859b2STodor Tomov 899ec6859b2STodor Tomov format.code = fse->code; 900ec6859b2STodor Tomov format.width = 1; 901ec6859b2STodor Tomov format.height = 1; 902ec6859b2STodor Tomov csid_try_format(csid, cfg, fse->pad, &format, fse->which); 903ec6859b2STodor Tomov fse->min_width = format.width; 904ec6859b2STodor Tomov fse->min_height = format.height; 905ec6859b2STodor Tomov 906ec6859b2STodor Tomov if (format.code != fse->code) 907ec6859b2STodor Tomov return -EINVAL; 908ec6859b2STodor Tomov 909ec6859b2STodor Tomov format.code = fse->code; 910ec6859b2STodor Tomov format.width = -1; 911ec6859b2STodor Tomov format.height = -1; 912ec6859b2STodor Tomov csid_try_format(csid, cfg, fse->pad, &format, fse->which); 913ec6859b2STodor Tomov fse->max_width = format.width; 914ec6859b2STodor Tomov fse->max_height = format.height; 915ec6859b2STodor Tomov 916ec6859b2STodor Tomov return 0; 917ec6859b2STodor Tomov } 918ec6859b2STodor Tomov 919ec6859b2STodor Tomov /* 920ec6859b2STodor Tomov * csid_get_format - Handle get format by pads subdev method 921ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 922ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 923ec6859b2STodor Tomov * @fmt: pointer to v4l2 subdev format structure 924ec6859b2STodor Tomov * 925ec6859b2STodor Tomov * Return -EINVAL or zero on success 926ec6859b2STodor Tomov */ 927ec6859b2STodor Tomov static int csid_get_format(struct v4l2_subdev *sd, 928ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 929ec6859b2STodor Tomov struct v4l2_subdev_format *fmt) 930ec6859b2STodor Tomov { 931ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 932ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 933ec6859b2STodor Tomov 934ec6859b2STodor Tomov format = __csid_get_format(csid, cfg, fmt->pad, fmt->which); 935ec6859b2STodor Tomov if (format == NULL) 936ec6859b2STodor Tomov return -EINVAL; 937ec6859b2STodor Tomov 938ec6859b2STodor Tomov fmt->format = *format; 939ec6859b2STodor Tomov 940ec6859b2STodor Tomov return 0; 941ec6859b2STodor Tomov } 942ec6859b2STodor Tomov 943ec6859b2STodor Tomov /* 944ec6859b2STodor Tomov * csid_set_format - Handle set format by pads subdev method 945ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 946ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 947ec6859b2STodor Tomov * @fmt: pointer to v4l2 subdev format structure 948ec6859b2STodor Tomov * 949ec6859b2STodor Tomov * Return -EINVAL or zero on success 950ec6859b2STodor Tomov */ 951ec6859b2STodor Tomov static int csid_set_format(struct v4l2_subdev *sd, 952ec6859b2STodor Tomov struct v4l2_subdev_pad_config *cfg, 953ec6859b2STodor Tomov struct v4l2_subdev_format *fmt) 954ec6859b2STodor Tomov { 955ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 956ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 957ec6859b2STodor Tomov 958ec6859b2STodor Tomov format = __csid_get_format(csid, cfg, fmt->pad, fmt->which); 959ec6859b2STodor Tomov if (format == NULL) 960ec6859b2STodor Tomov return -EINVAL; 961ec6859b2STodor Tomov 962ec6859b2STodor Tomov csid_try_format(csid, cfg, fmt->pad, &fmt->format, fmt->which); 963ec6859b2STodor Tomov *format = fmt->format; 964ec6859b2STodor Tomov 965ec6859b2STodor Tomov /* Propagate the format from sink to source */ 966ec6859b2STodor Tomov if (fmt->pad == MSM_CSID_PAD_SINK) { 967ec6859b2STodor Tomov format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SRC, 968ec6859b2STodor Tomov fmt->which); 969ec6859b2STodor Tomov 970ec6859b2STodor Tomov *format = fmt->format; 971ec6859b2STodor Tomov csid_try_format(csid, cfg, MSM_CSID_PAD_SRC, format, 972ec6859b2STodor Tomov fmt->which); 973ec6859b2STodor Tomov } 974ec6859b2STodor Tomov 975ec6859b2STodor Tomov return 0; 976ec6859b2STodor Tomov } 977ec6859b2STodor Tomov 978ec6859b2STodor Tomov /* 979ec6859b2STodor Tomov * csid_init_formats - Initialize formats on all pads 980ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 981ec6859b2STodor Tomov * @fh: V4L2 subdev file handle 982ec6859b2STodor Tomov * 983ec6859b2STodor Tomov * Initialize all pad formats with default values. 984ec6859b2STodor Tomov * 985ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 986ec6859b2STodor Tomov */ 987ec6859b2STodor Tomov static int csid_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 988ec6859b2STodor Tomov { 989ec6859b2STodor Tomov struct v4l2_subdev_format format = { 990ec6859b2STodor Tomov .pad = MSM_CSID_PAD_SINK, 991ec6859b2STodor Tomov .which = fh ? V4L2_SUBDEV_FORMAT_TRY : 992ec6859b2STodor Tomov V4L2_SUBDEV_FORMAT_ACTIVE, 993ec6859b2STodor Tomov .format = { 994ec6859b2STodor Tomov .code = MEDIA_BUS_FMT_UYVY8_2X8, 995ec6859b2STodor Tomov .width = 1920, 996ec6859b2STodor Tomov .height = 1080 997ec6859b2STodor Tomov } 998ec6859b2STodor Tomov }; 999ec6859b2STodor Tomov 1000ec6859b2STodor Tomov return csid_set_format(sd, fh ? fh->pad : NULL, &format); 1001ec6859b2STodor Tomov } 1002ec6859b2STodor Tomov 1003ec6859b2STodor Tomov static const char * const csid_test_pattern_menu[] = { 1004ec6859b2STodor Tomov "Disabled", 1005ec6859b2STodor Tomov "Incrementing", 1006ec6859b2STodor Tomov "Alternating 0x55/0xAA", 1007ec6859b2STodor Tomov "All Zeros 0x00", 1008ec6859b2STodor Tomov "All Ones 0xFF", 1009ec6859b2STodor Tomov "Pseudo-random Data", 1010ec6859b2STodor Tomov }; 1011ec6859b2STodor Tomov 1012ec6859b2STodor Tomov /* 1013ec6859b2STodor Tomov * csid_set_test_pattern - Set test generator's pattern mode 1014ec6859b2STodor Tomov * @csid: CSID device 1015ec6859b2STodor Tomov * @value: desired test pattern mode 1016ec6859b2STodor Tomov * 1017ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 1018ec6859b2STodor Tomov */ 1019ec6859b2STodor Tomov static int csid_set_test_pattern(struct csid_device *csid, s32 value) 1020ec6859b2STodor Tomov { 1021ec6859b2STodor Tomov struct csid_testgen_config *tg = &csid->testgen; 1022ec6859b2STodor Tomov 1023ec6859b2STodor Tomov /* If CSID is linked to CSIPHY, do not allow to enable test generator */ 1024ec6859b2STodor Tomov if (value && media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) 1025ec6859b2STodor Tomov return -EBUSY; 1026ec6859b2STodor Tomov 1027ec6859b2STodor Tomov tg->enabled = !!value; 1028ec6859b2STodor Tomov 1029ec6859b2STodor Tomov switch (value) { 1030ec6859b2STodor Tomov case 1: 1031ec6859b2STodor Tomov tg->payload_mode = CSID_PAYLOAD_MODE_INCREMENTING; 1032ec6859b2STodor Tomov break; 1033ec6859b2STodor Tomov case 2: 1034ec6859b2STodor Tomov tg->payload_mode = CSID_PAYLOAD_MODE_ALTERNATING_55_AA; 1035ec6859b2STodor Tomov break; 1036ec6859b2STodor Tomov case 3: 1037ec6859b2STodor Tomov tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ZEROES; 1038ec6859b2STodor Tomov break; 1039ec6859b2STodor Tomov case 4: 1040ec6859b2STodor Tomov tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ONES; 1041ec6859b2STodor Tomov break; 1042ec6859b2STodor Tomov case 5: 1043ec6859b2STodor Tomov tg->payload_mode = CSID_PAYLOAD_MODE_RANDOM; 1044ec6859b2STodor Tomov break; 1045ec6859b2STodor Tomov } 1046ec6859b2STodor Tomov 1047ec6859b2STodor Tomov return 0; 1048ec6859b2STodor Tomov } 1049ec6859b2STodor Tomov 1050ec6859b2STodor Tomov /* 1051ec6859b2STodor Tomov * csid_s_ctrl - Handle set control subdev method 1052ec6859b2STodor Tomov * @ctrl: pointer to v4l2 control structure 1053ec6859b2STodor Tomov * 1054ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 1055ec6859b2STodor Tomov */ 1056ec6859b2STodor Tomov static int csid_s_ctrl(struct v4l2_ctrl *ctrl) 1057ec6859b2STodor Tomov { 1058ec6859b2STodor Tomov struct csid_device *csid = container_of(ctrl->handler, 1059ec6859b2STodor Tomov struct csid_device, ctrls); 1060ec6859b2STodor Tomov int ret = -EINVAL; 1061ec6859b2STodor Tomov 1062ec6859b2STodor Tomov switch (ctrl->id) { 1063ec6859b2STodor Tomov case V4L2_CID_TEST_PATTERN: 1064ec6859b2STodor Tomov ret = csid_set_test_pattern(csid, ctrl->val); 1065ec6859b2STodor Tomov break; 1066ec6859b2STodor Tomov } 1067ec6859b2STodor Tomov 1068ec6859b2STodor Tomov return ret; 1069ec6859b2STodor Tomov } 1070ec6859b2STodor Tomov 1071ec6859b2STodor Tomov static const struct v4l2_ctrl_ops csid_ctrl_ops = { 1072ec6859b2STodor Tomov .s_ctrl = csid_s_ctrl, 1073ec6859b2STodor Tomov }; 1074ec6859b2STodor Tomov 1075ec6859b2STodor Tomov /* 1076ec6859b2STodor Tomov * msm_csid_subdev_init - Initialize CSID device structure and resources 1077ec6859b2STodor Tomov * @csid: CSID device 1078ec6859b2STodor Tomov * @res: CSID module resources table 1079ec6859b2STodor Tomov * @id: CSID module id 1080ec6859b2STodor Tomov * 1081ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 1082ec6859b2STodor Tomov */ 10839c3e59deSTodor Tomov int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, 1084ec6859b2STodor Tomov const struct resources *res, u8 id) 1085ec6859b2STodor Tomov { 10869c3e59deSTodor Tomov struct device *dev = camss->dev; 1087ec6859b2STodor Tomov struct platform_device *pdev = to_platform_device(dev); 1088ec6859b2STodor Tomov struct resource *r; 1089ec6859b2STodor Tomov int i, j; 1090ec6859b2STodor Tomov int ret; 1091ec6859b2STodor Tomov 10929c3e59deSTodor Tomov csid->camss = camss; 1093ec6859b2STodor Tomov csid->id = id; 1094ec6859b2STodor Tomov 1095cba3819dSTodor Tomov if (camss->version == CAMSS_8x16) { 1096cba3819dSTodor Tomov csid->formats = csid_formats_8x16; 1097cba3819dSTodor Tomov csid->nformats = 1098cba3819dSTodor Tomov ARRAY_SIZE(csid_formats_8x16); 1099cba3819dSTodor Tomov } else if (camss->version == CAMSS_8x96) { 1100cba3819dSTodor Tomov csid->formats = csid_formats_8x96; 1101cba3819dSTodor Tomov csid->nformats = 1102cba3819dSTodor Tomov ARRAY_SIZE(csid_formats_8x96); 1103cba3819dSTodor Tomov } else { 1104cba3819dSTodor Tomov return -EINVAL; 1105cba3819dSTodor Tomov } 1106cba3819dSTodor Tomov 1107ec6859b2STodor Tomov /* Memory */ 1108ec6859b2STodor Tomov 1109ec6859b2STodor Tomov r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); 1110ec6859b2STodor Tomov csid->base = devm_ioremap_resource(dev, r); 1111ec6859b2STodor Tomov if (IS_ERR(csid->base)) { 1112ec6859b2STodor Tomov dev_err(dev, "could not map memory\n"); 1113ec6859b2STodor Tomov return PTR_ERR(csid->base); 1114ec6859b2STodor Tomov } 1115ec6859b2STodor Tomov 1116ec6859b2STodor Tomov /* Interrupt */ 1117ec6859b2STodor Tomov 1118ec6859b2STodor Tomov r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, 1119ec6859b2STodor Tomov res->interrupt[0]); 1120ec6859b2STodor Tomov if (!r) { 1121ec6859b2STodor Tomov dev_err(dev, "missing IRQ\n"); 1122ec6859b2STodor Tomov return -EINVAL; 1123ec6859b2STodor Tomov } 1124ec6859b2STodor Tomov 1125ec6859b2STodor Tomov csid->irq = r->start; 1126ec6859b2STodor Tomov snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d", 1127ec6859b2STodor Tomov dev_name(dev), MSM_CSID_NAME, csid->id); 1128ec6859b2STodor Tomov ret = devm_request_irq(dev, csid->irq, csid_isr, 1129ec6859b2STodor Tomov IRQF_TRIGGER_RISING, csid->irq_name, csid); 1130ec6859b2STodor Tomov if (ret < 0) { 1131ec6859b2STodor Tomov dev_err(dev, "request_irq failed: %d\n", ret); 1132ec6859b2STodor Tomov return ret; 1133ec6859b2STodor Tomov } 1134ec6859b2STodor Tomov 1135ec6859b2STodor Tomov disable_irq(csid->irq); 1136ec6859b2STodor Tomov 1137ec6859b2STodor Tomov /* Clocks */ 1138ec6859b2STodor Tomov 1139ec6859b2STodor Tomov csid->nclocks = 0; 1140ec6859b2STodor Tomov while (res->clock[csid->nclocks]) 1141ec6859b2STodor Tomov csid->nclocks++; 1142ec6859b2STodor Tomov 1143ec6859b2STodor Tomov csid->clock = devm_kcalloc(dev, csid->nclocks, sizeof(*csid->clock), 1144ec6859b2STodor Tomov GFP_KERNEL); 1145ec6859b2STodor Tomov if (!csid->clock) 1146ec6859b2STodor Tomov return -ENOMEM; 1147ec6859b2STodor Tomov 1148ec6859b2STodor Tomov for (i = 0; i < csid->nclocks; i++) { 1149ec6859b2STodor Tomov struct camss_clock *clock = &csid->clock[i]; 1150ec6859b2STodor Tomov 1151ec6859b2STodor Tomov clock->clk = devm_clk_get(dev, res->clock[i]); 1152ec6859b2STodor Tomov if (IS_ERR(clock->clk)) 1153ec6859b2STodor Tomov return PTR_ERR(clock->clk); 1154ec6859b2STodor Tomov 1155ec6859b2STodor Tomov clock->name = res->clock[i]; 1156ec6859b2STodor Tomov 1157ec6859b2STodor Tomov clock->nfreqs = 0; 1158ec6859b2STodor Tomov while (res->clock_rate[i][clock->nfreqs]) 1159ec6859b2STodor Tomov clock->nfreqs++; 1160ec6859b2STodor Tomov 1161ec6859b2STodor Tomov if (!clock->nfreqs) { 1162ec6859b2STodor Tomov clock->freq = NULL; 1163ec6859b2STodor Tomov continue; 1164ec6859b2STodor Tomov } 1165ec6859b2STodor Tomov 1166ec6859b2STodor Tomov clock->freq = devm_kcalloc(dev, 1167ec6859b2STodor Tomov clock->nfreqs, 1168ec6859b2STodor Tomov sizeof(*clock->freq), 1169ec6859b2STodor Tomov GFP_KERNEL); 1170ec6859b2STodor Tomov if (!clock->freq) 1171ec6859b2STodor Tomov return -ENOMEM; 1172ec6859b2STodor Tomov 1173ec6859b2STodor Tomov for (j = 0; j < clock->nfreqs; j++) 1174ec6859b2STodor Tomov clock->freq[j] = res->clock_rate[i][j]; 1175ec6859b2STodor Tomov } 1176ec6859b2STodor Tomov 1177ec6859b2STodor Tomov /* Regulator */ 1178ec6859b2STodor Tomov 1179ec6859b2STodor Tomov csid->vdda = devm_regulator_get(dev, res->regulator[0]); 1180ec6859b2STodor Tomov if (IS_ERR(csid->vdda)) { 1181ec6859b2STodor Tomov dev_err(dev, "could not get regulator\n"); 1182ec6859b2STodor Tomov return PTR_ERR(csid->vdda); 1183ec6859b2STodor Tomov } 1184ec6859b2STodor Tomov 1185ec6859b2STodor Tomov init_completion(&csid->reset_complete); 1186ec6859b2STodor Tomov 1187ec6859b2STodor Tomov return 0; 1188ec6859b2STodor Tomov } 1189ec6859b2STodor Tomov 1190ec6859b2STodor Tomov /* 1191ec6859b2STodor Tomov * msm_csid_get_csid_id - Get CSID HW module id 1192ec6859b2STodor Tomov * @entity: Pointer to CSID media entity structure 1193ec6859b2STodor Tomov * @id: Return CSID HW module id here 1194ec6859b2STodor Tomov */ 1195ec6859b2STodor Tomov void msm_csid_get_csid_id(struct media_entity *entity, u8 *id) 1196ec6859b2STodor Tomov { 1197ec6859b2STodor Tomov struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); 1198ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 1199ec6859b2STodor Tomov 1200ec6859b2STodor Tomov *id = csid->id; 1201ec6859b2STodor Tomov } 1202ec6859b2STodor Tomov 1203ec6859b2STodor Tomov /* 1204ec6859b2STodor Tomov * csid_get_lane_assign - Calculate CSI2 lane assign configuration parameter 1205ec6859b2STodor Tomov * @lane_cfg - CSI2 lane configuration 1206ec6859b2STodor Tomov * 1207ec6859b2STodor Tomov * Return lane assign 1208ec6859b2STodor Tomov */ 1209ec6859b2STodor Tomov static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg) 1210ec6859b2STodor Tomov { 1211ec6859b2STodor Tomov u32 lane_assign = 0; 1212ec6859b2STodor Tomov int i; 1213ec6859b2STodor Tomov 1214ec6859b2STodor Tomov for (i = 0; i < lane_cfg->num_data; i++) 1215ec6859b2STodor Tomov lane_assign |= lane_cfg->data[i].pos << (i * 4); 1216ec6859b2STodor Tomov 1217ec6859b2STodor Tomov return lane_assign; 1218ec6859b2STodor Tomov } 1219ec6859b2STodor Tomov 1220ec6859b2STodor Tomov /* 1221ec6859b2STodor Tomov * csid_link_setup - Setup CSID connections 1222ec6859b2STodor Tomov * @entity: Pointer to media entity structure 1223ec6859b2STodor Tomov * @local: Pointer to local pad 1224ec6859b2STodor Tomov * @remote: Pointer to remote pad 1225ec6859b2STodor Tomov * @flags: Link flags 1226ec6859b2STodor Tomov * 1227ec6859b2STodor Tomov * Return 0 on success 1228ec6859b2STodor Tomov */ 1229ec6859b2STodor Tomov static int csid_link_setup(struct media_entity *entity, 1230ec6859b2STodor Tomov const struct media_pad *local, 1231ec6859b2STodor Tomov const struct media_pad *remote, u32 flags) 1232ec6859b2STodor Tomov { 1233ec6859b2STodor Tomov if (flags & MEDIA_LNK_FL_ENABLED) 1234ec6859b2STodor Tomov if (media_entity_remote_pad(local)) 1235ec6859b2STodor Tomov return -EBUSY; 1236ec6859b2STodor Tomov 1237ec6859b2STodor Tomov if ((local->flags & MEDIA_PAD_FL_SINK) && 1238ec6859b2STodor Tomov (flags & MEDIA_LNK_FL_ENABLED)) { 1239ec6859b2STodor Tomov struct v4l2_subdev *sd; 1240ec6859b2STodor Tomov struct csid_device *csid; 1241ec6859b2STodor Tomov struct csiphy_device *csiphy; 1242ec6859b2STodor Tomov struct csiphy_lanes_cfg *lane_cfg; 1243ec6859b2STodor Tomov struct v4l2_subdev_format format = { 0 }; 1244ec6859b2STodor Tomov 1245ec6859b2STodor Tomov sd = media_entity_to_v4l2_subdev(entity); 1246ec6859b2STodor Tomov csid = v4l2_get_subdevdata(sd); 1247ec6859b2STodor Tomov 1248ec6859b2STodor Tomov /* If test generator is enabled */ 1249ec6859b2STodor Tomov /* do not allow a link from CSIPHY to CSID */ 1250ec6859b2STodor Tomov if (csid->testgen_mode->cur.val != 0) 1251ec6859b2STodor Tomov return -EBUSY; 1252ec6859b2STodor Tomov 1253ec6859b2STodor Tomov sd = media_entity_to_v4l2_subdev(remote->entity); 1254ec6859b2STodor Tomov csiphy = v4l2_get_subdevdata(sd); 1255ec6859b2STodor Tomov 1256ec6859b2STodor Tomov /* If a sensor is not linked to CSIPHY */ 1257ec6859b2STodor Tomov /* do no allow a link from CSIPHY to CSID */ 1258ec6859b2STodor Tomov if (!csiphy->cfg.csi2) 1259ec6859b2STodor Tomov return -EPERM; 1260ec6859b2STodor Tomov 1261ec6859b2STodor Tomov csid->phy.csiphy_id = csiphy->id; 1262ec6859b2STodor Tomov 1263ec6859b2STodor Tomov lane_cfg = &csiphy->cfg.csi2->lane_cfg; 1264ec6859b2STodor Tomov csid->phy.lane_cnt = lane_cfg->num_data; 1265ec6859b2STodor Tomov csid->phy.lane_assign = csid_get_lane_assign(lane_cfg); 1266ec6859b2STodor Tomov 1267ec6859b2STodor Tomov /* Reset format on source pad to sink pad format */ 1268ec6859b2STodor Tomov format.pad = MSM_CSID_PAD_SRC; 1269ec6859b2STodor Tomov format.which = V4L2_SUBDEV_FORMAT_ACTIVE; 1270ec6859b2STodor Tomov csid_set_format(&csid->subdev, NULL, &format); 1271ec6859b2STodor Tomov } 1272ec6859b2STodor Tomov 1273ec6859b2STodor Tomov return 0; 1274ec6859b2STodor Tomov } 1275ec6859b2STodor Tomov 1276ec6859b2STodor Tomov static const struct v4l2_subdev_core_ops csid_core_ops = { 1277ec6859b2STodor Tomov .s_power = csid_set_power, 1278988b3ae3STodor Tomov .subscribe_event = v4l2_ctrl_subdev_subscribe_event, 1279988b3ae3STodor Tomov .unsubscribe_event = v4l2_event_subdev_unsubscribe, 1280ec6859b2STodor Tomov }; 1281ec6859b2STodor Tomov 1282ec6859b2STodor Tomov static const struct v4l2_subdev_video_ops csid_video_ops = { 1283ec6859b2STodor Tomov .s_stream = csid_set_stream, 1284ec6859b2STodor Tomov }; 1285ec6859b2STodor Tomov 1286ec6859b2STodor Tomov static const struct v4l2_subdev_pad_ops csid_pad_ops = { 1287ec6859b2STodor Tomov .enum_mbus_code = csid_enum_mbus_code, 1288ec6859b2STodor Tomov .enum_frame_size = csid_enum_frame_size, 1289ec6859b2STodor Tomov .get_fmt = csid_get_format, 1290ec6859b2STodor Tomov .set_fmt = csid_set_format, 1291ec6859b2STodor Tomov }; 1292ec6859b2STodor Tomov 1293ec6859b2STodor Tomov static const struct v4l2_subdev_ops csid_v4l2_ops = { 1294ec6859b2STodor Tomov .core = &csid_core_ops, 1295ec6859b2STodor Tomov .video = &csid_video_ops, 1296ec6859b2STodor Tomov .pad = &csid_pad_ops, 1297ec6859b2STodor Tomov }; 1298ec6859b2STodor Tomov 1299ec6859b2STodor Tomov static const struct v4l2_subdev_internal_ops csid_v4l2_internal_ops = { 1300ec6859b2STodor Tomov .open = csid_init_formats, 1301ec6859b2STodor Tomov }; 1302ec6859b2STodor Tomov 1303ec6859b2STodor Tomov static const struct media_entity_operations csid_media_ops = { 1304ec6859b2STodor Tomov .link_setup = csid_link_setup, 1305ec6859b2STodor Tomov .link_validate = v4l2_subdev_link_validate, 1306ec6859b2STodor Tomov }; 1307ec6859b2STodor Tomov 1308ec6859b2STodor Tomov /* 1309ec6859b2STodor Tomov * msm_csid_register_entity - Register subdev node for CSID module 1310ec6859b2STodor Tomov * @csid: CSID device 1311ec6859b2STodor Tomov * @v4l2_dev: V4L2 device 1312ec6859b2STodor Tomov * 1313ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 1314ec6859b2STodor Tomov */ 1315ec6859b2STodor Tomov int msm_csid_register_entity(struct csid_device *csid, 1316ec6859b2STodor Tomov struct v4l2_device *v4l2_dev) 1317ec6859b2STodor Tomov { 1318ec6859b2STodor Tomov struct v4l2_subdev *sd = &csid->subdev; 1319ec6859b2STodor Tomov struct media_pad *pads = csid->pads; 13209c3e59deSTodor Tomov struct device *dev = csid->camss->dev; 1321ec6859b2STodor Tomov int ret; 1322ec6859b2STodor Tomov 1323ec6859b2STodor Tomov v4l2_subdev_init(sd, &csid_v4l2_ops); 1324ec6859b2STodor Tomov sd->internal_ops = &csid_v4l2_internal_ops; 1325988b3ae3STodor Tomov sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | 1326988b3ae3STodor Tomov V4L2_SUBDEV_FL_HAS_EVENTS; 1327ec6859b2STodor Tomov snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", 1328ec6859b2STodor Tomov MSM_CSID_NAME, csid->id); 1329ec6859b2STodor Tomov v4l2_set_subdevdata(sd, csid); 1330ec6859b2STodor Tomov 1331ec6859b2STodor Tomov ret = v4l2_ctrl_handler_init(&csid->ctrls, 1); 1332ec6859b2STodor Tomov if (ret < 0) { 1333ec6859b2STodor Tomov dev_err(dev, "Failed to init ctrl handler: %d\n", ret); 1334ec6859b2STodor Tomov return ret; 1335ec6859b2STodor Tomov } 1336ec6859b2STodor Tomov 1337ec6859b2STodor Tomov csid->testgen_mode = v4l2_ctrl_new_std_menu_items(&csid->ctrls, 1338ec6859b2STodor Tomov &csid_ctrl_ops, V4L2_CID_TEST_PATTERN, 1339ec6859b2STodor Tomov ARRAY_SIZE(csid_test_pattern_menu) - 1, 0, 0, 1340ec6859b2STodor Tomov csid_test_pattern_menu); 1341ec6859b2STodor Tomov 1342ec6859b2STodor Tomov if (csid->ctrls.error) { 1343ec6859b2STodor Tomov dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error); 1344ec6859b2STodor Tomov ret = csid->ctrls.error; 1345ec6859b2STodor Tomov goto free_ctrl; 1346ec6859b2STodor Tomov } 1347ec6859b2STodor Tomov 1348ec6859b2STodor Tomov csid->subdev.ctrl_handler = &csid->ctrls; 1349ec6859b2STodor Tomov 1350ec6859b2STodor Tomov ret = csid_init_formats(sd, NULL); 1351ec6859b2STodor Tomov if (ret < 0) { 1352ec6859b2STodor Tomov dev_err(dev, "Failed to init format: %d\n", ret); 1353ec6859b2STodor Tomov goto free_ctrl; 1354ec6859b2STodor Tomov } 1355ec6859b2STodor Tomov 1356ec6859b2STodor Tomov pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 1357ec6859b2STodor Tomov pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; 1358ec6859b2STodor Tomov 1359ec6859b2STodor Tomov sd->entity.function = MEDIA_ENT_F_IO_V4L; 1360ec6859b2STodor Tomov sd->entity.ops = &csid_media_ops; 1361ec6859b2STodor Tomov ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads); 1362ec6859b2STodor Tomov if (ret < 0) { 1363ec6859b2STodor Tomov dev_err(dev, "Failed to init media entity: %d\n", ret); 1364ec6859b2STodor Tomov goto free_ctrl; 1365ec6859b2STodor Tomov } 1366ec6859b2STodor Tomov 1367ec6859b2STodor Tomov ret = v4l2_device_register_subdev(v4l2_dev, sd); 1368ec6859b2STodor Tomov if (ret < 0) { 1369ec6859b2STodor Tomov dev_err(dev, "Failed to register subdev: %d\n", ret); 1370ec6859b2STodor Tomov goto media_cleanup; 1371ec6859b2STodor Tomov } 1372ec6859b2STodor Tomov 1373ec6859b2STodor Tomov return 0; 1374ec6859b2STodor Tomov 1375ec6859b2STodor Tomov media_cleanup: 1376ec6859b2STodor Tomov media_entity_cleanup(&sd->entity); 1377ec6859b2STodor Tomov free_ctrl: 1378ec6859b2STodor Tomov v4l2_ctrl_handler_free(&csid->ctrls); 1379ec6859b2STodor Tomov 1380ec6859b2STodor Tomov return ret; 1381ec6859b2STodor Tomov } 1382ec6859b2STodor Tomov 1383ec6859b2STodor Tomov /* 1384ec6859b2STodor Tomov * msm_csid_unregister_entity - Unregister CSID module subdev node 1385ec6859b2STodor Tomov * @csid: CSID device 1386ec6859b2STodor Tomov */ 1387ec6859b2STodor Tomov void msm_csid_unregister_entity(struct csid_device *csid) 1388ec6859b2STodor Tomov { 1389ec6859b2STodor Tomov v4l2_device_unregister_subdev(&csid->subdev); 1390ec6859b2STodor Tomov media_entity_cleanup(&csid->subdev.entity); 1391ec6859b2STodor Tomov v4l2_ctrl_handler_free(&csid->ctrls); 1392ec6859b2STodor Tomov } 1393