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> 133799eca5SArnd 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" 25e19b14b1SRobert Foss #include "camss-csid-gen1.h" 26ec6859b2STodor Tomov #include "camss.h" 27ec6859b2STodor Tomov 28b4436a18SJonathan Marek /* offset of CSID registers in VFE region for VFE 480 */ 29b4436a18SJonathan Marek #define VFE_480_CSID_OFFSET 0x1200 30b4436a18SJonathan Marek #define VFE_480_LITE_CSID_OFFSET 0x200 31b4436a18SJonathan Marek 32ec6859b2STodor Tomov #define MSM_CSID_NAME "msm_csid" 33ec6859b2STodor Tomov 3476005817SRobert Foss const char * const csid_testgen_modes[] = { 3576005817SRobert Foss "Disabled", 3676005817SRobert Foss "Incrementing", 3776005817SRobert Foss "Alternating 0x55/0xAA", 3876005817SRobert Foss "All Zeros 0x00", 3976005817SRobert Foss "All Ones 0xFF", 4076005817SRobert Foss "Pseudo-random Data", 4176005817SRobert Foss "User Specified", 42eebe6d00SRobert Foss "Complex pattern", 43eebe6d00SRobert Foss "Color box", 44eebe6d00SRobert Foss "Color bars", 4576005817SRobert Foss NULL 46ec6859b2STodor Tomov }; 47ec6859b2STodor Tomov 4876005817SRobert Foss u32 csid_find_code(u32 *codes, unsigned int ncodes, 4976005817SRobert Foss unsigned int match_format_idx, u32 match_code) 507e37f47fSTodor Tomov { 517e37f47fSTodor Tomov int i; 527e37f47fSTodor Tomov 5376005817SRobert Foss if (!match_code && (match_format_idx >= ncodes)) 547e37f47fSTodor Tomov return 0; 557e37f47fSTodor Tomov 5676005817SRobert Foss for (i = 0; i < ncodes; i++) 5776005817SRobert Foss if (match_code) { 5876005817SRobert Foss if (codes[i] == match_code) 5976005817SRobert Foss return match_code; 607e37f47fSTodor Tomov } else { 6176005817SRobert Foss if (i == match_format_idx) 6276005817SRobert Foss return codes[i]; 637e37f47fSTodor Tomov } 647e37f47fSTodor Tomov 6576005817SRobert Foss return codes[0]; 667e37f47fSTodor Tomov } 677e37f47fSTodor Tomov 6876005817SRobert Foss const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats, 6976005817SRobert Foss unsigned int nformats, 70cba3819dSTodor Tomov u32 code) 71ec6859b2STodor Tomov { 72ec6859b2STodor Tomov unsigned int i; 73ec6859b2STodor Tomov 7476005817SRobert Foss for (i = 0; i < nformats; i++) 75cba3819dSTodor Tomov if (code == formats[i].code) 76cba3819dSTodor Tomov return &formats[i]; 77ec6859b2STodor Tomov 78ec6859b2STodor Tomov WARN(1, "Unknown format\n"); 79ec6859b2STodor Tomov 80cba3819dSTodor Tomov return &formats[0]; 81ec6859b2STodor Tomov } 82ec6859b2STodor Tomov 83ec6859b2STodor Tomov /* 84ec6859b2STodor Tomov * csid_set_clock_rates - Calculate and set clock rates on CSID module 85ec6859b2STodor Tomov * @csiphy: CSID device 86ec6859b2STodor Tomov */ 87ec6859b2STodor Tomov static int csid_set_clock_rates(struct csid_device *csid) 88ec6859b2STodor Tomov { 899c3e59deSTodor Tomov struct device *dev = csid->camss->dev; 9078c2cc28SAndrey Konovalov const struct csid_format *fmt; 9178c2cc28SAndrey Konovalov s64 link_freq; 92ec6859b2STodor Tomov int i, j; 93ec6859b2STodor Tomov int ret; 94ec6859b2STodor Tomov 9578c2cc28SAndrey Konovalov fmt = csid_get_fmt_entry(csid->formats, csid->nformats, 9678c2cc28SAndrey Konovalov csid->fmt[MSM_CSIPHY_PAD_SINK].code); 9778c2cc28SAndrey Konovalov link_freq = camss_get_link_freq(&csid->subdev.entity, fmt->bpp, 9878c2cc28SAndrey Konovalov csid->phy.lane_cnt); 9978c2cc28SAndrey Konovalov if (link_freq < 0) 10078c2cc28SAndrey Konovalov link_freq = 0; 101ec6859b2STodor Tomov 102ec6859b2STodor Tomov for (i = 0; i < csid->nclocks; i++) { 103ec6859b2STodor Tomov struct camss_clock *clock = &csid->clock[i]; 104ec6859b2STodor Tomov 105ec6859b2STodor Tomov if (!strcmp(clock->name, "csi0") || 1069c3e59deSTodor Tomov !strcmp(clock->name, "csi1") || 1079c3e59deSTodor Tomov !strcmp(clock->name, "csi2") || 1089c3e59deSTodor Tomov !strcmp(clock->name, "csi3")) { 10978c2cc28SAndrey Konovalov u64 min_rate = link_freq / 4; 110ec6859b2STodor Tomov long rate; 111ec6859b2STodor Tomov 112ec6859b2STodor Tomov camss_add_clock_margin(&min_rate); 113ec6859b2STodor Tomov 114ec6859b2STodor Tomov for (j = 0; j < clock->nfreqs; j++) 115ec6859b2STodor Tomov if (min_rate < clock->freq[j]) 116ec6859b2STodor Tomov break; 117ec6859b2STodor Tomov 118ec6859b2STodor Tomov if (j == clock->nfreqs) { 119ec6859b2STodor Tomov dev_err(dev, 120ec6859b2STodor Tomov "Pixel clock is too high for CSID\n"); 121ec6859b2STodor Tomov return -EINVAL; 122ec6859b2STodor Tomov } 123ec6859b2STodor Tomov 124ec6859b2STodor Tomov /* if sensor pixel clock is not available */ 125ec6859b2STodor Tomov /* set highest possible CSID clock rate */ 126ec6859b2STodor Tomov if (min_rate == 0) 127ec6859b2STodor Tomov j = clock->nfreqs - 1; 128ec6859b2STodor Tomov 129ec6859b2STodor Tomov rate = clk_round_rate(clock->clk, clock->freq[j]); 130ec6859b2STodor Tomov if (rate < 0) { 131ec6859b2STodor Tomov dev_err(dev, "clk round rate failed: %ld\n", 132ec6859b2STodor Tomov rate); 133ec6859b2STodor Tomov return -EINVAL; 134ec6859b2STodor Tomov } 135ec6859b2STodor Tomov 136ec6859b2STodor Tomov ret = clk_set_rate(clock->clk, rate); 137ec6859b2STodor Tomov if (ret < 0) { 138ec6859b2STodor Tomov dev_err(dev, "clk set rate failed: %d\n", ret); 139ec6859b2STodor Tomov return ret; 140ec6859b2STodor Tomov } 141eebe6d00SRobert Foss } else if (clock->nfreqs) { 142eebe6d00SRobert Foss clk_set_rate(clock->clk, clock->freq[0]); 143ec6859b2STodor Tomov } 144ec6859b2STodor Tomov } 145ec6859b2STodor Tomov 146ec6859b2STodor Tomov return 0; 147ec6859b2STodor Tomov } 148ec6859b2STodor Tomov 149ec6859b2STodor Tomov /* 150ec6859b2STodor Tomov * csid_set_power - Power on/off CSID module 151ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 152ec6859b2STodor Tomov * @on: Requested power state 153ec6859b2STodor Tomov * 154ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 155ec6859b2STodor Tomov */ 156ec6859b2STodor Tomov static int csid_set_power(struct v4l2_subdev *sd, int on) 157ec6859b2STodor Tomov { 158ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 159c5af8db8SBryan O'Donoghue struct camss *camss = csid->camss; 160c5af8db8SBryan O'Donoghue struct device *dev = camss->dev; 161c5af8db8SBryan O'Donoghue struct vfe_device *vfe = &camss->vfe[csid->id]; 1620d814017SBryan O'Donoghue int ret = 0; 163ec6859b2STodor Tomov 164ec6859b2STodor Tomov if (on) { 165b2c2715eSBryan O'Donoghue /* 166b2c2715eSBryan O'Donoghue * From SDM845 onwards, the VFE needs to be powered on before 167b2c2715eSBryan O'Donoghue * switching on the CSID. Do so unconditionally, as there is no 168b2c2715eSBryan O'Donoghue * drawback in following the same powering order on older SoCs. 169b2c2715eSBryan O'Donoghue */ 170c5af8db8SBryan O'Donoghue ret = vfe_get(vfe); 171c5af8db8SBryan O'Donoghue if (ret < 0) 172c5af8db8SBryan O'Donoghue return ret; 173c5af8db8SBryan O'Donoghue 17409dfb36cSMauro Carvalho Chehab ret = pm_runtime_resume_and_get(dev); 17509dfb36cSMauro Carvalho Chehab if (ret < 0) 176ec6859b2STodor Tomov return ret; 177ec6859b2STodor Tomov 1780d814017SBryan O'Donoghue ret = regulator_bulk_enable(csid->num_supplies, 1790d814017SBryan O'Donoghue csid->supplies); 18002afa816STodor Tomov if (ret < 0) { 18102afa816STodor Tomov pm_runtime_put_sync(dev); 18202afa816STodor Tomov return ret; 18302afa816STodor Tomov } 18402afa816STodor Tomov 185ec6859b2STodor Tomov ret = csid_set_clock_rates(csid); 186ec6859b2STodor Tomov if (ret < 0) { 1870d814017SBryan O'Donoghue regulator_bulk_disable(csid->num_supplies, 1880d814017SBryan O'Donoghue csid->supplies); 18902afa816STodor Tomov pm_runtime_put_sync(dev); 190ec6859b2STodor Tomov return ret; 191ec6859b2STodor Tomov } 192ec6859b2STodor Tomov 193ec6859b2STodor Tomov ret = camss_enable_clocks(csid->nclocks, csid->clock, dev); 194ec6859b2STodor Tomov if (ret < 0) { 1950d814017SBryan O'Donoghue regulator_bulk_disable(csid->num_supplies, 1960d814017SBryan O'Donoghue csid->supplies); 19702afa816STodor Tomov pm_runtime_put_sync(dev); 198ec6859b2STodor Tomov return ret; 199ec6859b2STodor Tomov } 200ec6859b2STodor Tomov 2013c4ed72aSMilen Mitkov csid->phy.need_vc_update = true; 2023c4ed72aSMilen Mitkov 203ec6859b2STodor Tomov enable_irq(csid->irq); 204ec6859b2STodor Tomov 20576005817SRobert Foss ret = csid->ops->reset(csid); 206ec6859b2STodor Tomov if (ret < 0) { 207ec6859b2STodor Tomov disable_irq(csid->irq); 208ec6859b2STodor Tomov camss_disable_clocks(csid->nclocks, csid->clock); 2090d814017SBryan O'Donoghue regulator_bulk_disable(csid->num_supplies, 2100d814017SBryan O'Donoghue csid->supplies); 21102afa816STodor Tomov pm_runtime_put_sync(dev); 212ec6859b2STodor Tomov return ret; 213ec6859b2STodor Tomov } 214ec6859b2STodor Tomov 21576005817SRobert Foss csid->ops->hw_version(csid); 216ec6859b2STodor Tomov } else { 217ec6859b2STodor Tomov disable_irq(csid->irq); 218ec6859b2STodor Tomov camss_disable_clocks(csid->nclocks, csid->clock); 2190d814017SBryan O'Donoghue regulator_bulk_disable(csid->num_supplies, 2200d814017SBryan O'Donoghue csid->supplies); 22102afa816STodor Tomov pm_runtime_put_sync(dev); 222c5af8db8SBryan O'Donoghue vfe_put(vfe); 223ec6859b2STodor Tomov } 224ec6859b2STodor Tomov 225ec6859b2STodor Tomov return ret; 226ec6859b2STodor Tomov } 227ec6859b2STodor Tomov 228ec6859b2STodor Tomov /* 229ec6859b2STodor Tomov * csid_set_stream - Enable/disable streaming on CSID module 230ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 231ec6859b2STodor Tomov * @enable: Requested streaming state 232ec6859b2STodor Tomov * 233ec6859b2STodor Tomov * Main configuration of CSID module is also done here. 234ec6859b2STodor Tomov * 235ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 236ec6859b2STodor Tomov */ 237ec6859b2STodor Tomov static int csid_set_stream(struct v4l2_subdev *sd, int enable) 238ec6859b2STodor Tomov { 239ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 240ec6859b2STodor Tomov int ret; 241ec6859b2STodor Tomov 24276005817SRobert Foss if (enable) { 243ec6859b2STodor Tomov ret = v4l2_ctrl_handler_setup(&csid->ctrls); 244ec6859b2STodor Tomov if (ret < 0) { 2459c3e59deSTodor Tomov dev_err(csid->camss->dev, 246ec6859b2STodor Tomov "could not sync v4l2 controls: %d\n", ret); 247ec6859b2STodor Tomov return ret; 248ec6859b2STodor Tomov } 249ec6859b2STodor Tomov 25076005817SRobert Foss if (!csid->testgen.enabled && 251b2e44430SLaurent Pinchart !media_pad_remote_pad_first(&csid->pads[MSM_CSID_PAD_SINK])) 252ec6859b2STodor Tomov return -ENOLINK; 253ec6859b2STodor Tomov } 254ec6859b2STodor Tomov 2553c4ed72aSMilen Mitkov if (csid->phy.need_vc_update) { 25676005817SRobert Foss csid->ops->configure_stream(csid, enable); 2573c4ed72aSMilen Mitkov csid->phy.need_vc_update = false; 2583c4ed72aSMilen Mitkov } 259ec6859b2STodor Tomov 260ec6859b2STodor Tomov return 0; 261ec6859b2STodor Tomov } 262ec6859b2STodor Tomov 263ec6859b2STodor Tomov /* 264ec6859b2STodor Tomov * __csid_get_format - Get pointer to format structure 265ec6859b2STodor Tomov * @csid: CSID device 266c1d96814SLaurent Pinchart * @sd_state: V4L2 subdev state 267ec6859b2STodor Tomov * @pad: pad from which format is requested 268ec6859b2STodor Tomov * @which: TRY or ACTIVE format 269ec6859b2STodor Tomov * 270ec6859b2STodor Tomov * Return pointer to TRY or ACTIVE format structure 271ec6859b2STodor Tomov */ 272ec6859b2STodor Tomov static struct v4l2_mbus_framefmt * 273ec6859b2STodor Tomov __csid_get_format(struct csid_device *csid, 2740d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 275ec6859b2STodor Tomov unsigned int pad, 276ec6859b2STodor Tomov enum v4l2_subdev_format_whence which) 277ec6859b2STodor Tomov { 278ec6859b2STodor Tomov if (which == V4L2_SUBDEV_FORMAT_TRY) 279bc0e8d91SSakari Ailus return v4l2_subdev_state_get_format(sd_state, pad); 280ec6859b2STodor Tomov 281ec6859b2STodor Tomov return &csid->fmt[pad]; 282ec6859b2STodor Tomov } 283ec6859b2STodor Tomov 284ec6859b2STodor Tomov /* 285ec6859b2STodor Tomov * csid_try_format - Handle try format by pad subdev method 286ec6859b2STodor Tomov * @csid: CSID device 287c1d96814SLaurent Pinchart * @sd_state: V4L2 subdev state 288ec6859b2STodor Tomov * @pad: pad on which format is requested 289ec6859b2STodor Tomov * @fmt: pointer to v4l2 format structure 290ec6859b2STodor Tomov * @which: wanted subdev format 291ec6859b2STodor Tomov */ 292ec6859b2STodor Tomov static void csid_try_format(struct csid_device *csid, 2930d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 294ec6859b2STodor Tomov unsigned int pad, 295ec6859b2STodor Tomov struct v4l2_mbus_framefmt *fmt, 296ec6859b2STodor Tomov enum v4l2_subdev_format_whence which) 297ec6859b2STodor Tomov { 298ec6859b2STodor Tomov unsigned int i; 299ec6859b2STodor Tomov 300ec6859b2STodor Tomov switch (pad) { 301ec6859b2STodor Tomov case MSM_CSID_PAD_SINK: 302ec6859b2STodor Tomov /* Set format on sink pad */ 303ec6859b2STodor Tomov 304cba3819dSTodor Tomov for (i = 0; i < csid->nformats; i++) 305cba3819dSTodor Tomov if (fmt->code == csid->formats[i].code) 306ec6859b2STodor Tomov break; 307ec6859b2STodor Tomov 308ec6859b2STodor Tomov /* If not found, use UYVY as default */ 309cba3819dSTodor Tomov if (i >= csid->nformats) 31089936bfbSMartin Dørum fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; 311ec6859b2STodor Tomov 312ec6859b2STodor Tomov fmt->width = clamp_t(u32, fmt->width, 1, 8191); 313ec6859b2STodor Tomov fmt->height = clamp_t(u32, fmt->height, 1, 8191); 314ec6859b2STodor Tomov 315ec6859b2STodor Tomov fmt->field = V4L2_FIELD_NONE; 316ec6859b2STodor Tomov fmt->colorspace = V4L2_COLORSPACE_SRGB; 317ec6859b2STodor Tomov 318ec6859b2STodor Tomov break; 319ec6859b2STodor Tomov 320ec6859b2STodor Tomov case MSM_CSID_PAD_SRC: 321ec6859b2STodor Tomov if (csid->testgen_mode->cur.val == 0) { 3227e37f47fSTodor Tomov /* Test generator is disabled, */ 3237e37f47fSTodor Tomov /* keep pad formats in sync */ 3247e37f47fSTodor Tomov u32 code = fmt->code; 325ec6859b2STodor Tomov 3260d346d2aSTomi Valkeinen *fmt = *__csid_get_format(csid, sd_state, 327ec6859b2STodor Tomov MSM_CSID_PAD_SINK, which); 32876005817SRobert Foss fmt->code = csid->ops->src_pad_code(csid, fmt->code, 0, code); 329ec6859b2STodor Tomov } else { 330ec6859b2STodor Tomov /* Test generator is enabled, set format on source */ 331ec6859b2STodor Tomov /* pad to allow test generator usage */ 332ec6859b2STodor Tomov 333cba3819dSTodor Tomov for (i = 0; i < csid->nformats; i++) 334cba3819dSTodor Tomov if (csid->formats[i].code == fmt->code) 335ec6859b2STodor Tomov break; 336ec6859b2STodor Tomov 337ec6859b2STodor Tomov /* If not found, use UYVY as default */ 338cba3819dSTodor Tomov if (i >= csid->nformats) 33989936bfbSMartin Dørum fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; 340ec6859b2STodor Tomov 341ec6859b2STodor Tomov fmt->width = clamp_t(u32, fmt->width, 1, 8191); 342ec6859b2STodor Tomov fmt->height = clamp_t(u32, fmt->height, 1, 8191); 343ec6859b2STodor Tomov 344ec6859b2STodor Tomov fmt->field = V4L2_FIELD_NONE; 345ec6859b2STodor Tomov } 346ec6859b2STodor Tomov break; 347ec6859b2STodor Tomov } 348ec6859b2STodor Tomov 349ec6859b2STodor Tomov fmt->colorspace = V4L2_COLORSPACE_SRGB; 350ec6859b2STodor Tomov } 351ec6859b2STodor Tomov 352ec6859b2STodor Tomov /* 353ec6859b2STodor Tomov * csid_enum_mbus_code - Handle pixel format enumeration 354ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 355c1d96814SLaurent Pinchart * @sd_state: V4L2 subdev state 356ec6859b2STodor Tomov * @code: pointer to v4l2_subdev_mbus_code_enum structure 357ec6859b2STodor Tomov * return -EINVAL or zero on success 358ec6859b2STodor Tomov */ 359ec6859b2STodor Tomov static int csid_enum_mbus_code(struct v4l2_subdev *sd, 3600d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 361ec6859b2STodor Tomov struct v4l2_subdev_mbus_code_enum *code) 362ec6859b2STodor Tomov { 363ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 364ec6859b2STodor Tomov 365ec6859b2STodor Tomov if (code->pad == MSM_CSID_PAD_SINK) { 366cba3819dSTodor Tomov if (code->index >= csid->nformats) 367ec6859b2STodor Tomov return -EINVAL; 368ec6859b2STodor Tomov 369cba3819dSTodor Tomov code->code = csid->formats[code->index].code; 370ec6859b2STodor Tomov } else { 371ec6859b2STodor Tomov if (csid->testgen_mode->cur.val == 0) { 3727e37f47fSTodor Tomov struct v4l2_mbus_framefmt *sink_fmt; 373ec6859b2STodor Tomov 3740d346d2aSTomi Valkeinen sink_fmt = __csid_get_format(csid, sd_state, 3757e37f47fSTodor Tomov MSM_CSID_PAD_SINK, 376ec6859b2STodor Tomov code->which); 377ec6859b2STodor Tomov 37876005817SRobert Foss code->code = csid->ops->src_pad_code(csid, sink_fmt->code, 3797e37f47fSTodor Tomov code->index, 0); 3807e37f47fSTodor Tomov if (!code->code) 3817e37f47fSTodor Tomov return -EINVAL; 382ec6859b2STodor Tomov } else { 383cba3819dSTodor Tomov if (code->index >= csid->nformats) 384ec6859b2STodor Tomov return -EINVAL; 385ec6859b2STodor Tomov 386cba3819dSTodor Tomov code->code = csid->formats[code->index].code; 387ec6859b2STodor Tomov } 388ec6859b2STodor Tomov } 389ec6859b2STodor Tomov 390ec6859b2STodor Tomov return 0; 391ec6859b2STodor Tomov } 392ec6859b2STodor Tomov 393ec6859b2STodor Tomov /* 394ec6859b2STodor Tomov * csid_enum_frame_size - Handle frame size enumeration 395ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 396c1d96814SLaurent Pinchart * @sd_state: V4L2 subdev state 397ec6859b2STodor Tomov * @fse: pointer to v4l2_subdev_frame_size_enum structure 398ec6859b2STodor Tomov * return -EINVAL or zero on success 399ec6859b2STodor Tomov */ 400ec6859b2STodor Tomov static int csid_enum_frame_size(struct v4l2_subdev *sd, 4010d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 402ec6859b2STodor Tomov struct v4l2_subdev_frame_size_enum *fse) 403ec6859b2STodor Tomov { 404ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 405ec6859b2STodor Tomov struct v4l2_mbus_framefmt format; 406ec6859b2STodor Tomov 407ec6859b2STodor Tomov if (fse->index != 0) 408ec6859b2STodor Tomov return -EINVAL; 409ec6859b2STodor Tomov 410ec6859b2STodor Tomov format.code = fse->code; 411ec6859b2STodor Tomov format.width = 1; 412ec6859b2STodor Tomov format.height = 1; 4130d346d2aSTomi Valkeinen csid_try_format(csid, sd_state, fse->pad, &format, fse->which); 414ec6859b2STodor Tomov fse->min_width = format.width; 415ec6859b2STodor Tomov fse->min_height = format.height; 416ec6859b2STodor Tomov 417ec6859b2STodor Tomov if (format.code != fse->code) 418ec6859b2STodor Tomov return -EINVAL; 419ec6859b2STodor Tomov 420ec6859b2STodor Tomov format.code = fse->code; 421ec6859b2STodor Tomov format.width = -1; 422ec6859b2STodor Tomov format.height = -1; 4230d346d2aSTomi Valkeinen csid_try_format(csid, sd_state, fse->pad, &format, fse->which); 424ec6859b2STodor Tomov fse->max_width = format.width; 425ec6859b2STodor Tomov fse->max_height = format.height; 426ec6859b2STodor Tomov 427ec6859b2STodor Tomov return 0; 428ec6859b2STodor Tomov } 429ec6859b2STodor Tomov 430ec6859b2STodor Tomov /* 431ec6859b2STodor Tomov * csid_get_format - Handle get format by pads subdev method 432ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 433c1d96814SLaurent Pinchart * @sd_state: V4L2 subdev state 434ec6859b2STodor Tomov * @fmt: pointer to v4l2 subdev format structure 435ec6859b2STodor Tomov * 436ec6859b2STodor Tomov * Return -EINVAL or zero on success 437ec6859b2STodor Tomov */ 438ec6859b2STodor Tomov static int csid_get_format(struct v4l2_subdev *sd, 4390d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 440ec6859b2STodor Tomov struct v4l2_subdev_format *fmt) 441ec6859b2STodor Tomov { 442ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 443ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 444ec6859b2STodor Tomov 4450d346d2aSTomi Valkeinen format = __csid_get_format(csid, sd_state, fmt->pad, fmt->which); 446ec6859b2STodor Tomov if (format == NULL) 447ec6859b2STodor Tomov return -EINVAL; 448ec6859b2STodor Tomov 449ec6859b2STodor Tomov fmt->format = *format; 450ec6859b2STodor Tomov 451ec6859b2STodor Tomov return 0; 452ec6859b2STodor Tomov } 453ec6859b2STodor Tomov 454ec6859b2STodor Tomov /* 455ec6859b2STodor Tomov * csid_set_format - Handle set format by pads subdev method 456ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 457c1d96814SLaurent Pinchart * @sd_state: V4L2 subdev state 458ec6859b2STodor Tomov * @fmt: pointer to v4l2 subdev format structure 459ec6859b2STodor Tomov * 460ec6859b2STodor Tomov * Return -EINVAL or zero on success 461ec6859b2STodor Tomov */ 462ec6859b2STodor Tomov static int csid_set_format(struct v4l2_subdev *sd, 4630d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 464ec6859b2STodor Tomov struct v4l2_subdev_format *fmt) 465ec6859b2STodor Tomov { 466ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 467ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 4683c4ed72aSMilen Mitkov int i; 469ec6859b2STodor Tomov 4700d346d2aSTomi Valkeinen format = __csid_get_format(csid, sd_state, fmt->pad, fmt->which); 471ec6859b2STodor Tomov if (format == NULL) 472ec6859b2STodor Tomov return -EINVAL; 473ec6859b2STodor Tomov 4740d346d2aSTomi Valkeinen csid_try_format(csid, sd_state, fmt->pad, &fmt->format, fmt->which); 475ec6859b2STodor Tomov *format = fmt->format; 476ec6859b2STodor Tomov 4773c4ed72aSMilen Mitkov /* Propagate the format from sink to source pads */ 478ec6859b2STodor Tomov if (fmt->pad == MSM_CSID_PAD_SINK) { 4793c4ed72aSMilen Mitkov for (i = MSM_CSID_PAD_FIRST_SRC; i < MSM_CSID_PADS_NUM; ++i) { 4803c4ed72aSMilen Mitkov format = __csid_get_format(csid, sd_state, i, fmt->which); 481ec6859b2STodor Tomov 482ec6859b2STodor Tomov *format = fmt->format; 4833c4ed72aSMilen Mitkov csid_try_format(csid, sd_state, i, format, fmt->which); 4843c4ed72aSMilen Mitkov } 485ec6859b2STodor Tomov } 486ec6859b2STodor Tomov 487ec6859b2STodor Tomov return 0; 488ec6859b2STodor Tomov } 489ec6859b2STodor Tomov 490ec6859b2STodor Tomov /* 491ec6859b2STodor Tomov * csid_init_formats - Initialize formats on all pads 492ec6859b2STodor Tomov * @sd: CSID V4L2 subdevice 493ec6859b2STodor Tomov * @fh: V4L2 subdev file handle 494ec6859b2STodor Tomov * 495ec6859b2STodor Tomov * Initialize all pad formats with default values. 496ec6859b2STodor Tomov * 497ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 498ec6859b2STodor Tomov */ 499ec6859b2STodor Tomov static int csid_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 500ec6859b2STodor Tomov { 501ec6859b2STodor Tomov struct v4l2_subdev_format format = { 502ec6859b2STodor Tomov .pad = MSM_CSID_PAD_SINK, 503ec6859b2STodor Tomov .which = fh ? V4L2_SUBDEV_FORMAT_TRY : 504ec6859b2STodor Tomov V4L2_SUBDEV_FORMAT_ACTIVE, 505ec6859b2STodor Tomov .format = { 50689936bfbSMartin Dørum .code = MEDIA_BUS_FMT_UYVY8_1X16, 507ec6859b2STodor Tomov .width = 1920, 508ec6859b2STodor Tomov .height = 1080 509ec6859b2STodor Tomov } 510ec6859b2STodor Tomov }; 511ec6859b2STodor Tomov 5120d346d2aSTomi Valkeinen return csid_set_format(sd, fh ? fh->state : NULL, &format); 513ec6859b2STodor Tomov } 514ec6859b2STodor Tomov 515ec6859b2STodor Tomov /* 516ec6859b2STodor Tomov * csid_set_test_pattern - Set test generator's pattern mode 517ec6859b2STodor Tomov * @csid: CSID device 518ec6859b2STodor Tomov * @value: desired test pattern mode 519ec6859b2STodor Tomov * 520ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 521ec6859b2STodor Tomov */ 522ec6859b2STodor Tomov static int csid_set_test_pattern(struct csid_device *csid, s32 value) 523ec6859b2STodor Tomov { 524ec6859b2STodor Tomov struct csid_testgen_config *tg = &csid->testgen; 525ec6859b2STodor Tomov 526ec6859b2STodor Tomov /* If CSID is linked to CSIPHY, do not allow to enable test generator */ 527b2e44430SLaurent Pinchart if (value && media_pad_remote_pad_first(&csid->pads[MSM_CSID_PAD_SINK])) 528ec6859b2STodor Tomov return -EBUSY; 529ec6859b2STodor Tomov 530ec6859b2STodor Tomov tg->enabled = !!value; 531ec6859b2STodor Tomov 53276005817SRobert Foss return csid->ops->configure_testgen_pattern(csid, value); 533ec6859b2STodor Tomov } 534ec6859b2STodor Tomov 535ec6859b2STodor Tomov /* 536ec6859b2STodor Tomov * csid_s_ctrl - Handle set control subdev method 537ec6859b2STodor Tomov * @ctrl: pointer to v4l2 control structure 538ec6859b2STodor Tomov * 539ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 540ec6859b2STodor Tomov */ 541ec6859b2STodor Tomov static int csid_s_ctrl(struct v4l2_ctrl *ctrl) 542ec6859b2STodor Tomov { 543ec6859b2STodor Tomov struct csid_device *csid = container_of(ctrl->handler, 544ec6859b2STodor Tomov struct csid_device, ctrls); 545ec6859b2STodor Tomov int ret = -EINVAL; 546ec6859b2STodor Tomov 547ec6859b2STodor Tomov switch (ctrl->id) { 548ec6859b2STodor Tomov case V4L2_CID_TEST_PATTERN: 549ec6859b2STodor Tomov ret = csid_set_test_pattern(csid, ctrl->val); 550ec6859b2STodor Tomov break; 551ec6859b2STodor Tomov } 552ec6859b2STodor Tomov 553ec6859b2STodor Tomov return ret; 554ec6859b2STodor Tomov } 555ec6859b2STodor Tomov 556ec6859b2STodor Tomov static const struct v4l2_ctrl_ops csid_ctrl_ops = { 557ec6859b2STodor Tomov .s_ctrl = csid_s_ctrl, 558ec6859b2STodor Tomov }; 559ec6859b2STodor Tomov 560ec6859b2STodor Tomov /* 561ec6859b2STodor Tomov * msm_csid_subdev_init - Initialize CSID device structure and resources 562ec6859b2STodor Tomov * @csid: CSID device 563ec6859b2STodor Tomov * @res: CSID module resources table 564ec6859b2STodor Tomov * @id: CSID module id 565ec6859b2STodor Tomov * 566ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 567ec6859b2STodor Tomov */ 5689c3e59deSTodor Tomov int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, 5691643b787SBryan O'Donoghue const struct camss_subdev_resources *res, u8 id) 570ec6859b2STodor Tomov { 5719c3e59deSTodor Tomov struct device *dev = camss->dev; 572ec6859b2STodor Tomov struct platform_device *pdev = to_platform_device(dev); 573ec6859b2STodor Tomov int i, j; 574ec6859b2STodor Tomov int ret; 575ec6859b2STodor Tomov 5769c3e59deSTodor Tomov csid->camss = camss; 577ec6859b2STodor Tomov csid->id = id; 578c23c7998SBryan O'Donoghue csid->ops = res->ops; 579ec6859b2STodor Tomov 58076005817SRobert Foss csid->ops->subdev_init(csid); 581cba3819dSTodor Tomov 582ec6859b2STodor Tomov /* Memory */ 583ec6859b2STodor Tomov 5845900b051SBryan O'Donoghue if (camss->res->version == CAMSS_8250) { 585b4436a18SJonathan Marek /* for titan 480, CSID registers are inside the VFE region, 586b4436a18SJonathan Marek * between the VFE "top" and "bus" registers. this requires 587b4436a18SJonathan Marek * VFE to be initialized before CSID 588b4436a18SJonathan Marek */ 589b4436a18SJonathan Marek if (id >= 2) /* VFE/CSID lite */ 590b4436a18SJonathan Marek csid->base = camss->vfe[id].base + VFE_480_LITE_CSID_OFFSET; 591b4436a18SJonathan Marek else 592b4436a18SJonathan Marek csid->base = camss->vfe[id].base + VFE_480_CSID_OFFSET; 593b4436a18SJonathan Marek } else { 594414e0a64Sdingsenjie csid->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); 5956c0ed6d4SYang Yingliang if (IS_ERR(csid->base)) 596ec6859b2STodor Tomov return PTR_ERR(csid->base); 597b4436a18SJonathan Marek } 598ec6859b2STodor Tomov 599ec6859b2STodor Tomov /* Interrupt */ 600ec6859b2STodor Tomov 601b416be3aSLad Prabhakar ret = platform_get_irq_byname(pdev, res->interrupt[0]); 602b416be3aSLad Prabhakar if (ret < 0) 603b416be3aSLad Prabhakar return ret; 604ec6859b2STodor Tomov 605b416be3aSLad Prabhakar csid->irq = ret; 606ec6859b2STodor Tomov snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d", 607ec6859b2STodor Tomov dev_name(dev), MSM_CSID_NAME, csid->id); 60876005817SRobert Foss ret = devm_request_irq(dev, csid->irq, csid->ops->isr, 60914480e8dSTian Tao IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN, 61014480e8dSTian Tao csid->irq_name, csid); 611ec6859b2STodor Tomov if (ret < 0) { 612ec6859b2STodor Tomov dev_err(dev, "request_irq failed: %d\n", ret); 613ec6859b2STodor Tomov return ret; 614ec6859b2STodor Tomov } 615ec6859b2STodor Tomov 616ec6859b2STodor Tomov /* Clocks */ 617ec6859b2STodor Tomov 618ec6859b2STodor Tomov csid->nclocks = 0; 619ec6859b2STodor Tomov while (res->clock[csid->nclocks]) 620ec6859b2STodor Tomov csid->nclocks++; 621ec6859b2STodor Tomov 622ec6859b2STodor Tomov csid->clock = devm_kcalloc(dev, csid->nclocks, sizeof(*csid->clock), 623ec6859b2STodor Tomov GFP_KERNEL); 624ec6859b2STodor Tomov if (!csid->clock) 625ec6859b2STodor Tomov return -ENOMEM; 626ec6859b2STodor Tomov 627ec6859b2STodor Tomov for (i = 0; i < csid->nclocks; i++) { 628ec6859b2STodor Tomov struct camss_clock *clock = &csid->clock[i]; 629ec6859b2STodor Tomov 630ec6859b2STodor Tomov clock->clk = devm_clk_get(dev, res->clock[i]); 631ec6859b2STodor Tomov if (IS_ERR(clock->clk)) 632ec6859b2STodor Tomov return PTR_ERR(clock->clk); 633ec6859b2STodor Tomov 634ec6859b2STodor Tomov clock->name = res->clock[i]; 635ec6859b2STodor Tomov 636ec6859b2STodor Tomov clock->nfreqs = 0; 637ec6859b2STodor Tomov while (res->clock_rate[i][clock->nfreqs]) 638ec6859b2STodor Tomov clock->nfreqs++; 639ec6859b2STodor Tomov 640ec6859b2STodor Tomov if (!clock->nfreqs) { 641ec6859b2STodor Tomov clock->freq = NULL; 642ec6859b2STodor Tomov continue; 643ec6859b2STodor Tomov } 644ec6859b2STodor Tomov 645ec6859b2STodor Tomov clock->freq = devm_kcalloc(dev, 646ec6859b2STodor Tomov clock->nfreqs, 647ec6859b2STodor Tomov sizeof(*clock->freq), 648ec6859b2STodor Tomov GFP_KERNEL); 649ec6859b2STodor Tomov if (!clock->freq) 650ec6859b2STodor Tomov return -ENOMEM; 651ec6859b2STodor Tomov 652ec6859b2STodor Tomov for (j = 0; j < clock->nfreqs; j++) 653ec6859b2STodor Tomov clock->freq[j] = res->clock_rate[i][j]; 654ec6859b2STodor Tomov } 655ec6859b2STodor Tomov 656ec6859b2STodor Tomov /* Regulator */ 6570d814017SBryan O'Donoghue for (i = 0; i < ARRAY_SIZE(res->regulators); i++) { 6580d814017SBryan O'Donoghue if (res->regulators[i]) 6590d814017SBryan O'Donoghue csid->num_supplies++; 660ec6859b2STodor Tomov } 661ec6859b2STodor Tomov 6620d814017SBryan O'Donoghue if (csid->num_supplies) { 6630d814017SBryan O'Donoghue csid->supplies = devm_kmalloc_array(camss->dev, 6640d814017SBryan O'Donoghue csid->num_supplies, 6654c25384dSYang Yingliang sizeof(*csid->supplies), 6660d814017SBryan O'Donoghue GFP_KERNEL); 6670d814017SBryan O'Donoghue if (!csid->supplies) 6680d814017SBryan O'Donoghue return -ENOMEM; 6690d814017SBryan O'Donoghue } 6700d814017SBryan O'Donoghue 6710d814017SBryan O'Donoghue for (i = 0; i < csid->num_supplies; i++) 6720d814017SBryan O'Donoghue csid->supplies[i].supply = res->regulators[i]; 6730d814017SBryan O'Donoghue 6740d814017SBryan O'Donoghue ret = devm_regulator_bulk_get(camss->dev, csid->num_supplies, 6750d814017SBryan O'Donoghue csid->supplies); 6760d814017SBryan O'Donoghue if (ret) 6770d814017SBryan O'Donoghue return ret; 6780d814017SBryan O'Donoghue 679ec6859b2STodor Tomov init_completion(&csid->reset_complete); 680ec6859b2STodor Tomov 681ec6859b2STodor Tomov return 0; 682ec6859b2STodor Tomov } 683ec6859b2STodor Tomov 684ec6859b2STodor Tomov /* 685ec6859b2STodor Tomov * msm_csid_get_csid_id - Get CSID HW module id 686ec6859b2STodor Tomov * @entity: Pointer to CSID media entity structure 687ec6859b2STodor Tomov * @id: Return CSID HW module id here 688ec6859b2STodor Tomov */ 689ec6859b2STodor Tomov void msm_csid_get_csid_id(struct media_entity *entity, u8 *id) 690ec6859b2STodor Tomov { 691ec6859b2STodor Tomov struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); 692ec6859b2STodor Tomov struct csid_device *csid = v4l2_get_subdevdata(sd); 693ec6859b2STodor Tomov 694ec6859b2STodor Tomov *id = csid->id; 695ec6859b2STodor Tomov } 696ec6859b2STodor Tomov 697ec6859b2STodor Tomov /* 698ec6859b2STodor Tomov * csid_get_lane_assign - Calculate CSI2 lane assign configuration parameter 699ec6859b2STodor Tomov * @lane_cfg - CSI2 lane configuration 700ec6859b2STodor Tomov * 701ec6859b2STodor Tomov * Return lane assign 702ec6859b2STodor Tomov */ 703ec6859b2STodor Tomov static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg) 704ec6859b2STodor Tomov { 705ec6859b2STodor Tomov u32 lane_assign = 0; 706ec6859b2STodor Tomov int i; 707ec6859b2STodor Tomov 708ec6859b2STodor Tomov for (i = 0; i < lane_cfg->num_data; i++) 709ec6859b2STodor Tomov lane_assign |= lane_cfg->data[i].pos << (i * 4); 710ec6859b2STodor Tomov 711ec6859b2STodor Tomov return lane_assign; 712ec6859b2STodor Tomov } 713ec6859b2STodor Tomov 714ec6859b2STodor Tomov /* 715ec6859b2STodor Tomov * csid_link_setup - Setup CSID connections 716ec6859b2STodor Tomov * @entity: Pointer to media entity structure 717ec6859b2STodor Tomov * @local: Pointer to local pad 718ec6859b2STodor Tomov * @remote: Pointer to remote pad 719ec6859b2STodor Tomov * @flags: Link flags 720ec6859b2STodor Tomov * 721ec6859b2STodor Tomov * Return 0 on success 722ec6859b2STodor Tomov */ 723ec6859b2STodor Tomov static int csid_link_setup(struct media_entity *entity, 724ec6859b2STodor Tomov const struct media_pad *local, 725ec6859b2STodor Tomov const struct media_pad *remote, u32 flags) 726ec6859b2STodor Tomov { 727ec6859b2STodor Tomov if (flags & MEDIA_LNK_FL_ENABLED) 728b2e44430SLaurent Pinchart if (media_pad_remote_pad_first(local)) 729ec6859b2STodor Tomov return -EBUSY; 730ec6859b2STodor Tomov 731ec6859b2STodor Tomov if ((local->flags & MEDIA_PAD_FL_SINK) && 732ec6859b2STodor Tomov (flags & MEDIA_LNK_FL_ENABLED)) { 733ec6859b2STodor Tomov struct v4l2_subdev *sd; 734ec6859b2STodor Tomov struct csid_device *csid; 735ec6859b2STodor Tomov struct csiphy_device *csiphy; 736ec6859b2STodor Tomov struct csiphy_lanes_cfg *lane_cfg; 737ec6859b2STodor Tomov 738ec6859b2STodor Tomov sd = media_entity_to_v4l2_subdev(entity); 739ec6859b2STodor Tomov csid = v4l2_get_subdevdata(sd); 740ec6859b2STodor Tomov 741ec6859b2STodor Tomov /* If test generator is enabled */ 742ec6859b2STodor Tomov /* do not allow a link from CSIPHY to CSID */ 743ec6859b2STodor Tomov if (csid->testgen_mode->cur.val != 0) 744ec6859b2STodor Tomov return -EBUSY; 745ec6859b2STodor Tomov 746ec6859b2STodor Tomov sd = media_entity_to_v4l2_subdev(remote->entity); 747ec6859b2STodor Tomov csiphy = v4l2_get_subdevdata(sd); 748ec6859b2STodor Tomov 749ec6859b2STodor Tomov /* If a sensor is not linked to CSIPHY */ 750ec6859b2STodor Tomov /* do no allow a link from CSIPHY to CSID */ 751ec6859b2STodor Tomov if (!csiphy->cfg.csi2) 752ec6859b2STodor Tomov return -EPERM; 753ec6859b2STodor Tomov 754ec6859b2STodor Tomov csid->phy.csiphy_id = csiphy->id; 755ec6859b2STodor Tomov 756ec6859b2STodor Tomov lane_cfg = &csiphy->cfg.csi2->lane_cfg; 757ec6859b2STodor Tomov csid->phy.lane_cnt = lane_cfg->num_data; 758ec6859b2STodor Tomov csid->phy.lane_assign = csid_get_lane_assign(lane_cfg); 7593c4ed72aSMilen Mitkov } 7603c4ed72aSMilen Mitkov /* Decide which virtual channels to enable based on which source pads are enabled */ 7613c4ed72aSMilen Mitkov if (local->flags & MEDIA_PAD_FL_SOURCE) { 7623c4ed72aSMilen Mitkov struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); 7633c4ed72aSMilen Mitkov struct csid_device *csid = v4l2_get_subdevdata(sd); 7643c4ed72aSMilen Mitkov struct device *dev = csid->camss->dev; 765ec6859b2STodor Tomov 7663c4ed72aSMilen Mitkov if (flags & MEDIA_LNK_FL_ENABLED) 7673c4ed72aSMilen Mitkov csid->phy.en_vc |= BIT(local->index - 1); 7683c4ed72aSMilen Mitkov else 7693c4ed72aSMilen Mitkov csid->phy.en_vc &= ~BIT(local->index - 1); 7703c4ed72aSMilen Mitkov 7713c4ed72aSMilen Mitkov csid->phy.need_vc_update = true; 7723c4ed72aSMilen Mitkov 7733c4ed72aSMilen Mitkov dev_dbg(dev, "%s: Enabled CSID virtual channels mask 0x%x\n", 7743c4ed72aSMilen Mitkov __func__, csid->phy.en_vc); 775ec6859b2STodor Tomov } 776ec6859b2STodor Tomov 777ec6859b2STodor Tomov return 0; 778ec6859b2STodor Tomov } 779ec6859b2STodor Tomov 780ec6859b2STodor Tomov static const struct v4l2_subdev_core_ops csid_core_ops = { 781ec6859b2STodor Tomov .s_power = csid_set_power, 782988b3ae3STodor Tomov .subscribe_event = v4l2_ctrl_subdev_subscribe_event, 783988b3ae3STodor Tomov .unsubscribe_event = v4l2_event_subdev_unsubscribe, 784ec6859b2STodor Tomov }; 785ec6859b2STodor Tomov 786ec6859b2STodor Tomov static const struct v4l2_subdev_video_ops csid_video_ops = { 787ec6859b2STodor Tomov .s_stream = csid_set_stream, 788ec6859b2STodor Tomov }; 789ec6859b2STodor Tomov 790ec6859b2STodor Tomov static const struct v4l2_subdev_pad_ops csid_pad_ops = { 791ec6859b2STodor Tomov .enum_mbus_code = csid_enum_mbus_code, 792ec6859b2STodor Tomov .enum_frame_size = csid_enum_frame_size, 793ec6859b2STodor Tomov .get_fmt = csid_get_format, 794ec6859b2STodor Tomov .set_fmt = csid_set_format, 795ec6859b2STodor Tomov }; 796ec6859b2STodor Tomov 797ec6859b2STodor Tomov static const struct v4l2_subdev_ops csid_v4l2_ops = { 798ec6859b2STodor Tomov .core = &csid_core_ops, 799ec6859b2STodor Tomov .video = &csid_video_ops, 800ec6859b2STodor Tomov .pad = &csid_pad_ops, 801ec6859b2STodor Tomov }; 802ec6859b2STodor Tomov 803ec6859b2STodor Tomov static const struct v4l2_subdev_internal_ops csid_v4l2_internal_ops = { 804ec6859b2STodor Tomov .open = csid_init_formats, 805ec6859b2STodor Tomov }; 806ec6859b2STodor Tomov 807ec6859b2STodor Tomov static const struct media_entity_operations csid_media_ops = { 808ec6859b2STodor Tomov .link_setup = csid_link_setup, 809ec6859b2STodor Tomov .link_validate = v4l2_subdev_link_validate, 810ec6859b2STodor Tomov }; 811ec6859b2STodor Tomov 812ec6859b2STodor Tomov /* 813ec6859b2STodor Tomov * msm_csid_register_entity - Register subdev node for CSID module 814ec6859b2STodor Tomov * @csid: CSID device 815ec6859b2STodor Tomov * @v4l2_dev: V4L2 device 816ec6859b2STodor Tomov * 817ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 818ec6859b2STodor Tomov */ 819ec6859b2STodor Tomov int msm_csid_register_entity(struct csid_device *csid, 820ec6859b2STodor Tomov struct v4l2_device *v4l2_dev) 821ec6859b2STodor Tomov { 822ec6859b2STodor Tomov struct v4l2_subdev *sd = &csid->subdev; 823ec6859b2STodor Tomov struct media_pad *pads = csid->pads; 8249c3e59deSTodor Tomov struct device *dev = csid->camss->dev; 8253c4ed72aSMilen Mitkov int i; 826ec6859b2STodor Tomov int ret; 827ec6859b2STodor Tomov 828ec6859b2STodor Tomov v4l2_subdev_init(sd, &csid_v4l2_ops); 829ec6859b2STodor Tomov sd->internal_ops = &csid_v4l2_internal_ops; 830988b3ae3STodor Tomov sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | 831988b3ae3STodor Tomov V4L2_SUBDEV_FL_HAS_EVENTS; 832ec6859b2STodor Tomov snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", 833ec6859b2STodor Tomov MSM_CSID_NAME, csid->id); 834ec6859b2STodor Tomov v4l2_set_subdevdata(sd, csid); 835ec6859b2STodor Tomov 836ec6859b2STodor Tomov ret = v4l2_ctrl_handler_init(&csid->ctrls, 1); 837ec6859b2STodor Tomov if (ret < 0) { 838ec6859b2STodor Tomov dev_err(dev, "Failed to init ctrl handler: %d\n", ret); 839ec6859b2STodor Tomov return ret; 840ec6859b2STodor Tomov } 841ec6859b2STodor Tomov 842ec6859b2STodor Tomov csid->testgen_mode = v4l2_ctrl_new_std_menu_items(&csid->ctrls, 843ec6859b2STodor Tomov &csid_ctrl_ops, V4L2_CID_TEST_PATTERN, 84476005817SRobert Foss csid->testgen.nmodes, 0, 0, 84576005817SRobert Foss csid->testgen.modes); 846ec6859b2STodor Tomov 847ec6859b2STodor Tomov if (csid->ctrls.error) { 848ec6859b2STodor Tomov dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error); 849ec6859b2STodor Tomov ret = csid->ctrls.error; 850ec6859b2STodor Tomov goto free_ctrl; 851ec6859b2STodor Tomov } 852ec6859b2STodor Tomov 853ec6859b2STodor Tomov csid->subdev.ctrl_handler = &csid->ctrls; 854ec6859b2STodor Tomov 855ec6859b2STodor Tomov ret = csid_init_formats(sd, NULL); 856ec6859b2STodor Tomov if (ret < 0) { 857ec6859b2STodor Tomov dev_err(dev, "Failed to init format: %d\n", ret); 858ec6859b2STodor Tomov goto free_ctrl; 859ec6859b2STodor Tomov } 860ec6859b2STodor Tomov 861ec6859b2STodor Tomov pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 8623c4ed72aSMilen Mitkov for (i = MSM_CSID_PAD_FIRST_SRC; i < MSM_CSID_PADS_NUM; ++i) 8633c4ed72aSMilen Mitkov pads[i].flags = MEDIA_PAD_FL_SOURCE; 864ec6859b2STodor Tomov 86550795910SAndrey Konovalov sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 866ec6859b2STodor Tomov sd->entity.ops = &csid_media_ops; 867ec6859b2STodor Tomov ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads); 868ec6859b2STodor Tomov if (ret < 0) { 869ec6859b2STodor Tomov dev_err(dev, "Failed to init media entity: %d\n", ret); 870ec6859b2STodor Tomov goto free_ctrl; 871ec6859b2STodor Tomov } 872ec6859b2STodor Tomov 873ec6859b2STodor Tomov ret = v4l2_device_register_subdev(v4l2_dev, sd); 874ec6859b2STodor Tomov if (ret < 0) { 875ec6859b2STodor Tomov dev_err(dev, "Failed to register subdev: %d\n", ret); 876ec6859b2STodor Tomov goto media_cleanup; 877ec6859b2STodor Tomov } 878ec6859b2STodor Tomov 879ec6859b2STodor Tomov return 0; 880ec6859b2STodor Tomov 881ec6859b2STodor Tomov media_cleanup: 882ec6859b2STodor Tomov media_entity_cleanup(&sd->entity); 883ec6859b2STodor Tomov free_ctrl: 884ec6859b2STodor Tomov v4l2_ctrl_handler_free(&csid->ctrls); 885ec6859b2STodor Tomov 886ec6859b2STodor Tomov return ret; 887ec6859b2STodor Tomov } 888ec6859b2STodor Tomov 889ec6859b2STodor Tomov /* 890ec6859b2STodor Tomov * msm_csid_unregister_entity - Unregister CSID module subdev node 891ec6859b2STodor Tomov * @csid: CSID device 892ec6859b2STodor Tomov */ 893ec6859b2STodor Tomov void msm_csid_unregister_entity(struct csid_device *csid) 894ec6859b2STodor Tomov { 895ec6859b2STodor Tomov v4l2_device_unregister_subdev(&csid->subdev); 896ec6859b2STodor Tomov media_entity_cleanup(&csid->subdev.entity); 897ec6859b2STodor Tomov v4l2_ctrl_handler_free(&csid->ctrls); 898ec6859b2STodor Tomov } 899*801ca0e7SMatti Lehtimäki 900*801ca0e7SMatti Lehtimäki inline bool csid_is_lite(struct csid_device *csid) 901*801ca0e7SMatti Lehtimäki { 902*801ca0e7SMatti Lehtimäki return csid->camss->res->csid_res[csid->id].is_lite; 903*801ca0e7SMatti Lehtimäki } 904