1b873663bSTodor Tomov // SPDX-License-Identifier: GPL-2.0 2ec6859b2STodor Tomov /* 3ec6859b2STodor Tomov * camss-csiphy.c 4ec6859b2STodor Tomov * 5ec6859b2STodor Tomov * Qualcomm MSM Camera Subsystem - CSIPHY Module 6ec6859b2STodor Tomov * 7ec6859b2STodor Tomov * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. 8ec6859b2STodor Tomov * Copyright (C) 2016-2018 Linaro Ltd. 9ec6859b2STodor Tomov */ 10ec6859b2STodor Tomov #include <linux/clk.h> 11ec6859b2STodor Tomov #include <linux/delay.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 <media/media-entity.h> 19ec6859b2STodor Tomov #include <media/v4l2-device.h> 20ec6859b2STodor Tomov #include <media/v4l2-subdev.h> 21ec6859b2STodor Tomov 22ec6859b2STodor Tomov #include "camss-csiphy.h" 23ec6859b2STodor Tomov #include "camss.h" 24ec6859b2STodor Tomov 25ec6859b2STodor Tomov #define MSM_CSIPHY_NAME "msm_csiphy" 26ec6859b2STodor Tomov 27cba3819dSTodor Tomov struct csiphy_format { 28ec6859b2STodor Tomov u32 code; 29ec6859b2STodor Tomov u8 bpp; 30cba3819dSTodor Tomov }; 31cba3819dSTodor Tomov 32cba3819dSTodor Tomov static const struct csiphy_format csiphy_formats_8x16[] = { 3389936bfbSMartin Dørum { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, 3489936bfbSMartin Dørum { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, 3589936bfbSMartin Dørum { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, 3689936bfbSMartin Dørum { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, 37cba3819dSTodor Tomov { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, 38cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, 39cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, 40cba3819dSTodor Tomov { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, 41cba3819dSTodor Tomov { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, 42cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, 43cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, 44cba3819dSTodor Tomov { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 45cba3819dSTodor Tomov { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, 46cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, 47cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, 48cba3819dSTodor Tomov { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 49cc8fe073STodor Tomov { MEDIA_BUS_FMT_Y10_1X10, 10 }, 50cba3819dSTodor Tomov }; 51cba3819dSTodor Tomov 52cba3819dSTodor Tomov static const struct csiphy_format csiphy_formats_8x96[] = { 5389936bfbSMartin Dørum { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, 5489936bfbSMartin Dørum { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, 5589936bfbSMartin Dørum { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, 5689936bfbSMartin Dørum { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, 57cba3819dSTodor Tomov { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, 58cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, 59cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, 60cba3819dSTodor Tomov { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, 61cba3819dSTodor Tomov { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, 62cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, 63cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, 64cba3819dSTodor Tomov { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 65cba3819dSTodor Tomov { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, 66cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, 67cba3819dSTodor Tomov { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, 68cba3819dSTodor Tomov { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 69f476fb56STodor Tomov { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, 70f476fb56STodor Tomov { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, 71f476fb56STodor Tomov { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, 72f476fb56STodor Tomov { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, 73cc8fe073STodor Tomov { MEDIA_BUS_FMT_Y10_1X10, 10 }, 74ec6859b2STodor Tomov }; 75ec6859b2STodor Tomov 762f8b6719SRobert Foss static const struct csiphy_format csiphy_formats_sdm845[] = { 7789936bfbSMartin Dørum { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, 7889936bfbSMartin Dørum { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, 7989936bfbSMartin Dørum { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, 8089936bfbSMartin Dørum { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, 812f8b6719SRobert Foss { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, 822f8b6719SRobert Foss { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, 832f8b6719SRobert Foss { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, 842f8b6719SRobert Foss { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, 852f8b6719SRobert Foss { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, 862f8b6719SRobert Foss { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, 872f8b6719SRobert Foss { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, 882f8b6719SRobert Foss { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 892f8b6719SRobert Foss { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, 902f8b6719SRobert Foss { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, 912f8b6719SRobert Foss { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, 922f8b6719SRobert Foss { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 932f8b6719SRobert Foss { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, 942f8b6719SRobert Foss { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, 952f8b6719SRobert Foss { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, 962f8b6719SRobert Foss { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, 97e53d6608SJonathan Marek { MEDIA_BUS_FMT_Y8_1X8, 8 }, 982f8b6719SRobert Foss { MEDIA_BUS_FMT_Y10_1X10, 10 }, 992f8b6719SRobert Foss }; 1002f8b6719SRobert Foss 101ec6859b2STodor Tomov /* 102ec6859b2STodor Tomov * csiphy_get_bpp - map media bus format to bits per pixel 103cba3819dSTodor Tomov * @formats: supported media bus formats array 104cba3819dSTodor Tomov * @nformats: size of @formats array 105ec6859b2STodor Tomov * @code: media bus format code 106ec6859b2STodor Tomov * 107ec6859b2STodor Tomov * Return number of bits per pixel 108ec6859b2STodor Tomov */ 109cba3819dSTodor Tomov static u8 csiphy_get_bpp(const struct csiphy_format *formats, 110cba3819dSTodor Tomov unsigned int nformats, u32 code) 111ec6859b2STodor Tomov { 112ec6859b2STodor Tomov unsigned int i; 113ec6859b2STodor Tomov 114cba3819dSTodor Tomov for (i = 0; i < nformats; i++) 115cba3819dSTodor Tomov if (code == formats[i].code) 116cba3819dSTodor Tomov return formats[i].bpp; 117ec6859b2STodor Tomov 118ec6859b2STodor Tomov WARN(1, "Unknown format\n"); 119ec6859b2STodor Tomov 120cba3819dSTodor Tomov return formats[0].bpp; 121ec6859b2STodor Tomov } 122ec6859b2STodor Tomov 123ec6859b2STodor Tomov /* 124ec6859b2STodor Tomov * csiphy_set_clock_rates - Calculate and set clock rates on CSIPHY module 125ec6859b2STodor Tomov * @csiphy: CSIPHY device 126ec6859b2STodor Tomov */ 127ec6859b2STodor Tomov static int csiphy_set_clock_rates(struct csiphy_device *csiphy) 128ec6859b2STodor Tomov { 1299c3e59deSTodor Tomov struct device *dev = csiphy->camss->dev; 13078c2cc28SAndrey Konovalov s64 link_freq; 131ec6859b2STodor Tomov int i, j; 132ec6859b2STodor Tomov int ret; 133ec6859b2STodor Tomov 13478c2cc28SAndrey Konovalov u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, 13578c2cc28SAndrey Konovalov csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); 13678c2cc28SAndrey Konovalov u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; 13778c2cc28SAndrey Konovalov 13878c2cc28SAndrey Konovalov link_freq = camss_get_link_freq(&csiphy->subdev.entity, bpp, num_lanes); 13978c2cc28SAndrey Konovalov if (link_freq < 0) 14078c2cc28SAndrey Konovalov link_freq = 0; 141ec6859b2STodor Tomov 142ec6859b2STodor Tomov for (i = 0; i < csiphy->nclocks; i++) { 143ec6859b2STodor Tomov struct camss_clock *clock = &csiphy->clock[i]; 144ec6859b2STodor Tomov 1454863b93cSAngeloGioacchino Del Regno if (csiphy->rate_set[i]) { 14678c2cc28SAndrey Konovalov u64 min_rate = link_freq / 4; 147ec6859b2STodor Tomov long round_rate; 148ec6859b2STodor Tomov 149ec6859b2STodor Tomov camss_add_clock_margin(&min_rate); 150ec6859b2STodor Tomov 151ec6859b2STodor Tomov for (j = 0; j < clock->nfreqs; j++) 152ec6859b2STodor Tomov if (min_rate < clock->freq[j]) 153ec6859b2STodor Tomov break; 154ec6859b2STodor Tomov 155ec6859b2STodor Tomov if (j == clock->nfreqs) { 156ec6859b2STodor Tomov dev_err(dev, 157ec6859b2STodor Tomov "Pixel clock is too high for CSIPHY\n"); 158ec6859b2STodor Tomov return -EINVAL; 159ec6859b2STodor Tomov } 160ec6859b2STodor Tomov 161ec6859b2STodor Tomov /* if sensor pixel clock is not available */ 162ec6859b2STodor Tomov /* set highest possible CSIPHY clock rate */ 163ec6859b2STodor Tomov if (min_rate == 0) 164ec6859b2STodor Tomov j = clock->nfreqs - 1; 165ec6859b2STodor Tomov 166ec6859b2STodor Tomov round_rate = clk_round_rate(clock->clk, clock->freq[j]); 167ec6859b2STodor Tomov if (round_rate < 0) { 168ec6859b2STodor Tomov dev_err(dev, "clk round rate failed: %ld\n", 169ec6859b2STodor Tomov round_rate); 170ec6859b2STodor Tomov return -EINVAL; 171ec6859b2STodor Tomov } 172ec6859b2STodor Tomov 173ec6859b2STodor Tomov csiphy->timer_clk_rate = round_rate; 174ec6859b2STodor Tomov 175ec6859b2STodor Tomov ret = clk_set_rate(clock->clk, csiphy->timer_clk_rate); 176ec6859b2STodor Tomov if (ret < 0) { 177ec6859b2STodor Tomov dev_err(dev, "clk set rate failed: %d\n", ret); 178ec6859b2STodor Tomov return ret; 179ec6859b2STodor Tomov } 180ec6859b2STodor Tomov } 181ec6859b2STodor Tomov } 182ec6859b2STodor Tomov 183ec6859b2STodor Tomov return 0; 184ec6859b2STodor Tomov } 185ec6859b2STodor Tomov 186ec6859b2STodor Tomov /* 187ec6859b2STodor Tomov * csiphy_set_power - Power on/off CSIPHY module 188ec6859b2STodor Tomov * @sd: CSIPHY V4L2 subdevice 189ec6859b2STodor Tomov * @on: Requested power state 190ec6859b2STodor Tomov * 191ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 192ec6859b2STodor Tomov */ 193ec6859b2STodor Tomov static int csiphy_set_power(struct v4l2_subdev *sd, int on) 194ec6859b2STodor Tomov { 195ec6859b2STodor Tomov struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); 1969c3e59deSTodor Tomov struct device *dev = csiphy->camss->dev; 197ec6859b2STodor Tomov 198ec6859b2STodor Tomov if (on) { 199ec6859b2STodor Tomov int ret; 200ec6859b2STodor Tomov 20109dfb36cSMauro Carvalho Chehab ret = pm_runtime_resume_and_get(dev); 20209dfb36cSMauro Carvalho Chehab if (ret < 0) 203ec6859b2STodor Tomov return ret; 204ec6859b2STodor Tomov 20502afa816STodor Tomov ret = csiphy_set_clock_rates(csiphy); 20602afa816STodor Tomov if (ret < 0) { 20702afa816STodor Tomov pm_runtime_put_sync(dev); 208ec6859b2STodor Tomov return ret; 20902afa816STodor Tomov } 21002afa816STodor Tomov 21102afa816STodor Tomov ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev); 21202afa816STodor Tomov if (ret < 0) { 21302afa816STodor Tomov pm_runtime_put_sync(dev); 21402afa816STodor Tomov return ret; 21502afa816STodor Tomov } 216ec6859b2STodor Tomov 217ec6859b2STodor Tomov enable_irq(csiphy->irq); 218ec6859b2STodor Tomov 219516e8f0fSTodor Tomov csiphy->ops->reset(csiphy); 220ec6859b2STodor Tomov 221516e8f0fSTodor Tomov csiphy->ops->hw_version_read(csiphy, dev); 222ec6859b2STodor Tomov } else { 223ec6859b2STodor Tomov disable_irq(csiphy->irq); 224ec6859b2STodor Tomov 225ec6859b2STodor Tomov camss_disable_clocks(csiphy->nclocks, csiphy->clock); 22602afa816STodor Tomov 22702afa816STodor Tomov pm_runtime_put_sync(dev); 228ec6859b2STodor Tomov } 229ec6859b2STodor Tomov 230ec6859b2STodor Tomov return 0; 231ec6859b2STodor Tomov } 232ec6859b2STodor Tomov 233ec6859b2STodor Tomov /* 234ec6859b2STodor Tomov * csiphy_stream_on - Enable streaming on CSIPHY module 235ec6859b2STodor Tomov * @csiphy: CSIPHY device 236ec6859b2STodor Tomov * 237ec6859b2STodor Tomov * Helper function to enable streaming on CSIPHY module. 238ec6859b2STodor Tomov * Main configuration of CSIPHY module is also done here. 239ec6859b2STodor Tomov * 240ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 241ec6859b2STodor Tomov */ 242ec6859b2STodor Tomov static int csiphy_stream_on(struct csiphy_device *csiphy) 243ec6859b2STodor Tomov { 244ec6859b2STodor Tomov struct csiphy_config *cfg = &csiphy->cfg; 24578c2cc28SAndrey Konovalov s64 link_freq; 2464abb2130SRobert Foss u8 lane_mask = csiphy->ops->get_lane_mask(&cfg->csi2->lane_cfg); 247cba3819dSTodor Tomov u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, 248cba3819dSTodor Tomov csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); 24978c2cc28SAndrey Konovalov u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; 250ec6859b2STodor Tomov u8 val; 251ec6859b2STodor Tomov 25278c2cc28SAndrey Konovalov link_freq = camss_get_link_freq(&csiphy->subdev.entity, bpp, num_lanes); 25378c2cc28SAndrey Konovalov 25478c2cc28SAndrey Konovalov if (link_freq < 0) { 255516e8f0fSTodor Tomov dev_err(csiphy->camss->dev, 25678c2cc28SAndrey Konovalov "Cannot get CSI2 transmitter's link frequency\n"); 257516e8f0fSTodor Tomov return -EINVAL; 258516e8f0fSTodor Tomov } 259ec6859b2STodor Tomov 2602f8b6719SRobert Foss if (csiphy->base_clk_mux) { 261ec6859b2STodor Tomov val = readl_relaxed(csiphy->base_clk_mux); 262ec6859b2STodor Tomov if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) { 263ec6859b2STodor Tomov val &= ~0xf0; 264ec6859b2STodor Tomov val |= cfg->csid_id << 4; 265ec6859b2STodor Tomov } else { 266ec6859b2STodor Tomov val &= ~0xf; 267ec6859b2STodor Tomov val |= cfg->csid_id; 268ec6859b2STodor Tomov } 269ec6859b2STodor Tomov writel_relaxed(val, csiphy->base_clk_mux); 2702f8b6719SRobert Foss 2712f8b6719SRobert Foss /* Enforce reg write ordering between clk mux & lane enabling */ 2725ba913b3STodor Tomov wmb(); 2732f8b6719SRobert Foss } 274ec6859b2STodor Tomov 27578c2cc28SAndrey Konovalov csiphy->ops->lanes_enable(csiphy, cfg, link_freq, lane_mask); 276ec6859b2STodor Tomov 277ec6859b2STodor Tomov return 0; 278ec6859b2STodor Tomov } 279ec6859b2STodor Tomov 280ec6859b2STodor Tomov /* 281ec6859b2STodor Tomov * csiphy_stream_off - Disable streaming on CSIPHY module 282ec6859b2STodor Tomov * @csiphy: CSIPHY device 283ec6859b2STodor Tomov * 284ec6859b2STodor Tomov * Helper function to disable streaming on CSIPHY module 285ec6859b2STodor Tomov */ 286ec6859b2STodor Tomov static void csiphy_stream_off(struct csiphy_device *csiphy) 287ec6859b2STodor Tomov { 288369f81f3STodor Tomov csiphy->ops->lanes_disable(csiphy, &csiphy->cfg); 289ec6859b2STodor Tomov } 290ec6859b2STodor Tomov 291ec6859b2STodor Tomov 292ec6859b2STodor Tomov /* 293ec6859b2STodor Tomov * csiphy_set_stream - Enable/disable streaming on CSIPHY module 294ec6859b2STodor Tomov * @sd: CSIPHY V4L2 subdevice 295ec6859b2STodor Tomov * @enable: Requested streaming state 296ec6859b2STodor Tomov * 297ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 298ec6859b2STodor Tomov */ 299ec6859b2STodor Tomov static int csiphy_set_stream(struct v4l2_subdev *sd, int enable) 300ec6859b2STodor Tomov { 301ec6859b2STodor Tomov struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); 302ec6859b2STodor Tomov int ret = 0; 303ec6859b2STodor Tomov 304ec6859b2STodor Tomov if (enable) 305ec6859b2STodor Tomov ret = csiphy_stream_on(csiphy); 306ec6859b2STodor Tomov else 307ec6859b2STodor Tomov csiphy_stream_off(csiphy); 308ec6859b2STodor Tomov 309ec6859b2STodor Tomov return ret; 310ec6859b2STodor Tomov } 311ec6859b2STodor Tomov 312ec6859b2STodor Tomov /* 313ec6859b2STodor Tomov * __csiphy_get_format - Get pointer to format structure 314ec6859b2STodor Tomov * @csiphy: CSIPHY device 315ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 316ec6859b2STodor Tomov * @pad: pad from which format is requested 317ec6859b2STodor Tomov * @which: TRY or ACTIVE format 318ec6859b2STodor Tomov * 319ec6859b2STodor Tomov * Return pointer to TRY or ACTIVE format structure 320ec6859b2STodor Tomov */ 321ec6859b2STodor Tomov static struct v4l2_mbus_framefmt * 322ec6859b2STodor Tomov __csiphy_get_format(struct csiphy_device *csiphy, 3230d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 324ec6859b2STodor Tomov unsigned int pad, 325ec6859b2STodor Tomov enum v4l2_subdev_format_whence which) 326ec6859b2STodor Tomov { 327ec6859b2STodor Tomov if (which == V4L2_SUBDEV_FORMAT_TRY) 3280d346d2aSTomi Valkeinen return v4l2_subdev_get_try_format(&csiphy->subdev, sd_state, 3290d346d2aSTomi Valkeinen pad); 330ec6859b2STodor Tomov 331ec6859b2STodor Tomov return &csiphy->fmt[pad]; 332ec6859b2STodor Tomov } 333ec6859b2STodor Tomov 334ec6859b2STodor Tomov /* 335ec6859b2STodor Tomov * csiphy_try_format - Handle try format by pad subdev method 336ec6859b2STodor Tomov * @csiphy: CSIPHY device 337ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 338ec6859b2STodor Tomov * @pad: pad on which format is requested 339ec6859b2STodor Tomov * @fmt: pointer to v4l2 format structure 340ec6859b2STodor Tomov * @which: wanted subdev format 341ec6859b2STodor Tomov */ 342ec6859b2STodor Tomov static void csiphy_try_format(struct csiphy_device *csiphy, 3430d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 344ec6859b2STodor Tomov unsigned int pad, 345ec6859b2STodor Tomov struct v4l2_mbus_framefmt *fmt, 346ec6859b2STodor Tomov enum v4l2_subdev_format_whence which) 347ec6859b2STodor Tomov { 348ec6859b2STodor Tomov unsigned int i; 349ec6859b2STodor Tomov 350ec6859b2STodor Tomov switch (pad) { 351ec6859b2STodor Tomov case MSM_CSIPHY_PAD_SINK: 352ec6859b2STodor Tomov /* Set format on sink pad */ 353ec6859b2STodor Tomov 354cba3819dSTodor Tomov for (i = 0; i < csiphy->nformats; i++) 355cba3819dSTodor Tomov if (fmt->code == csiphy->formats[i].code) 356ec6859b2STodor Tomov break; 357ec6859b2STodor Tomov 358ec6859b2STodor Tomov /* If not found, use UYVY as default */ 359cba3819dSTodor Tomov if (i >= csiphy->nformats) 36089936bfbSMartin Dørum fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; 361ec6859b2STodor Tomov 362ec6859b2STodor Tomov fmt->width = clamp_t(u32, fmt->width, 1, 8191); 363ec6859b2STodor Tomov fmt->height = clamp_t(u32, fmt->height, 1, 8191); 364ec6859b2STodor Tomov 365ec6859b2STodor Tomov fmt->field = V4L2_FIELD_NONE; 366ec6859b2STodor Tomov fmt->colorspace = V4L2_COLORSPACE_SRGB; 367ec6859b2STodor Tomov 368ec6859b2STodor Tomov break; 369ec6859b2STodor Tomov 370ec6859b2STodor Tomov case MSM_CSIPHY_PAD_SRC: 371ec6859b2STodor Tomov /* Set and return a format same as sink pad */ 372ec6859b2STodor Tomov 3730d346d2aSTomi Valkeinen *fmt = *__csiphy_get_format(csiphy, sd_state, 3740d346d2aSTomi Valkeinen MSM_CSID_PAD_SINK, 375ec6859b2STodor Tomov which); 376ec6859b2STodor Tomov 377ec6859b2STodor Tomov break; 378ec6859b2STodor Tomov } 379ec6859b2STodor Tomov } 380ec6859b2STodor Tomov 381ec6859b2STodor Tomov /* 382ec6859b2STodor Tomov * csiphy_enum_mbus_code - Handle pixel format enumeration 383ec6859b2STodor Tomov * @sd: CSIPHY V4L2 subdevice 384ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 385ec6859b2STodor Tomov * @code: pointer to v4l2_subdev_mbus_code_enum structure 386ec6859b2STodor Tomov * return -EINVAL or zero on success 387ec6859b2STodor Tomov */ 388ec6859b2STodor Tomov static int csiphy_enum_mbus_code(struct v4l2_subdev *sd, 3890d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 390ec6859b2STodor Tomov struct v4l2_subdev_mbus_code_enum *code) 391ec6859b2STodor Tomov { 392ec6859b2STodor Tomov struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); 393ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 394ec6859b2STodor Tomov 395ec6859b2STodor Tomov if (code->pad == MSM_CSIPHY_PAD_SINK) { 396cba3819dSTodor Tomov if (code->index >= csiphy->nformats) 397ec6859b2STodor Tomov return -EINVAL; 398ec6859b2STodor Tomov 399cba3819dSTodor Tomov code->code = csiphy->formats[code->index].code; 400ec6859b2STodor Tomov } else { 401ec6859b2STodor Tomov if (code->index > 0) 402ec6859b2STodor Tomov return -EINVAL; 403ec6859b2STodor Tomov 4040d346d2aSTomi Valkeinen format = __csiphy_get_format(csiphy, sd_state, 4050d346d2aSTomi Valkeinen MSM_CSIPHY_PAD_SINK, 406ec6859b2STodor Tomov code->which); 407ec6859b2STodor Tomov 408ec6859b2STodor Tomov code->code = format->code; 409ec6859b2STodor Tomov } 410ec6859b2STodor Tomov 411ec6859b2STodor Tomov return 0; 412ec6859b2STodor Tomov } 413ec6859b2STodor Tomov 414ec6859b2STodor Tomov /* 415ec6859b2STodor Tomov * csiphy_enum_frame_size - Handle frame size enumeration 416ec6859b2STodor Tomov * @sd: CSIPHY V4L2 subdevice 417ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 418ec6859b2STodor Tomov * @fse: pointer to v4l2_subdev_frame_size_enum structure 419ec6859b2STodor Tomov * return -EINVAL or zero on success 420ec6859b2STodor Tomov */ 421ec6859b2STodor Tomov static int csiphy_enum_frame_size(struct v4l2_subdev *sd, 4220d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 423ec6859b2STodor Tomov struct v4l2_subdev_frame_size_enum *fse) 424ec6859b2STodor Tomov { 425ec6859b2STodor Tomov struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); 426ec6859b2STodor Tomov struct v4l2_mbus_framefmt format; 427ec6859b2STodor Tomov 428ec6859b2STodor Tomov if (fse->index != 0) 429ec6859b2STodor Tomov return -EINVAL; 430ec6859b2STodor Tomov 431ec6859b2STodor Tomov format.code = fse->code; 432ec6859b2STodor Tomov format.width = 1; 433ec6859b2STodor Tomov format.height = 1; 4340d346d2aSTomi Valkeinen csiphy_try_format(csiphy, sd_state, fse->pad, &format, fse->which); 435ec6859b2STodor Tomov fse->min_width = format.width; 436ec6859b2STodor Tomov fse->min_height = format.height; 437ec6859b2STodor Tomov 438ec6859b2STodor Tomov if (format.code != fse->code) 439ec6859b2STodor Tomov return -EINVAL; 440ec6859b2STodor Tomov 441ec6859b2STodor Tomov format.code = fse->code; 442ec6859b2STodor Tomov format.width = -1; 443ec6859b2STodor Tomov format.height = -1; 4440d346d2aSTomi Valkeinen csiphy_try_format(csiphy, sd_state, fse->pad, &format, fse->which); 445ec6859b2STodor Tomov fse->max_width = format.width; 446ec6859b2STodor Tomov fse->max_height = format.height; 447ec6859b2STodor Tomov 448ec6859b2STodor Tomov return 0; 449ec6859b2STodor Tomov } 450ec6859b2STodor Tomov 451ec6859b2STodor Tomov /* 452ec6859b2STodor Tomov * csiphy_get_format - Handle get format by pads subdev method 453ec6859b2STodor Tomov * @sd: CSIPHY V4L2 subdevice 454ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 455ec6859b2STodor Tomov * @fmt: pointer to v4l2 subdev format structure 456ec6859b2STodor Tomov * 457ec6859b2STodor Tomov * Return -EINVAL or zero on success 458ec6859b2STodor Tomov */ 459ec6859b2STodor Tomov static int csiphy_get_format(struct v4l2_subdev *sd, 4600d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 461ec6859b2STodor Tomov struct v4l2_subdev_format *fmt) 462ec6859b2STodor Tomov { 463ec6859b2STodor Tomov struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); 464ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 465ec6859b2STodor Tomov 4660d346d2aSTomi Valkeinen format = __csiphy_get_format(csiphy, sd_state, fmt->pad, fmt->which); 467ec6859b2STodor Tomov if (format == NULL) 468ec6859b2STodor Tomov return -EINVAL; 469ec6859b2STodor Tomov 470ec6859b2STodor Tomov fmt->format = *format; 471ec6859b2STodor Tomov 472ec6859b2STodor Tomov return 0; 473ec6859b2STodor Tomov } 474ec6859b2STodor Tomov 475ec6859b2STodor Tomov /* 476ec6859b2STodor Tomov * csiphy_set_format - Handle set format by pads subdev method 477ec6859b2STodor Tomov * @sd: CSIPHY V4L2 subdevice 478ec6859b2STodor Tomov * @cfg: V4L2 subdev pad configuration 479ec6859b2STodor Tomov * @fmt: pointer to v4l2 subdev format structure 480ec6859b2STodor Tomov * 481ec6859b2STodor Tomov * Return -EINVAL or zero on success 482ec6859b2STodor Tomov */ 483ec6859b2STodor Tomov static int csiphy_set_format(struct v4l2_subdev *sd, 4840d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 485ec6859b2STodor Tomov struct v4l2_subdev_format *fmt) 486ec6859b2STodor Tomov { 487ec6859b2STodor Tomov struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); 488ec6859b2STodor Tomov struct v4l2_mbus_framefmt *format; 489ec6859b2STodor Tomov 4900d346d2aSTomi Valkeinen format = __csiphy_get_format(csiphy, sd_state, fmt->pad, fmt->which); 491ec6859b2STodor Tomov if (format == NULL) 492ec6859b2STodor Tomov return -EINVAL; 493ec6859b2STodor Tomov 4940d346d2aSTomi Valkeinen csiphy_try_format(csiphy, sd_state, fmt->pad, &fmt->format, 4950d346d2aSTomi Valkeinen fmt->which); 496ec6859b2STodor Tomov *format = fmt->format; 497ec6859b2STodor Tomov 498ec6859b2STodor Tomov /* Propagate the format from sink to source */ 499ec6859b2STodor Tomov if (fmt->pad == MSM_CSIPHY_PAD_SINK) { 5000d346d2aSTomi Valkeinen format = __csiphy_get_format(csiphy, sd_state, 5010d346d2aSTomi Valkeinen MSM_CSIPHY_PAD_SRC, 502ec6859b2STodor Tomov fmt->which); 503ec6859b2STodor Tomov 504ec6859b2STodor Tomov *format = fmt->format; 5050d346d2aSTomi Valkeinen csiphy_try_format(csiphy, sd_state, MSM_CSIPHY_PAD_SRC, 5060d346d2aSTomi Valkeinen format, 507ec6859b2STodor Tomov fmt->which); 508ec6859b2STodor Tomov } 509ec6859b2STodor Tomov 510ec6859b2STodor Tomov return 0; 511ec6859b2STodor Tomov } 512ec6859b2STodor Tomov 513ec6859b2STodor Tomov /* 514ec6859b2STodor Tomov * csiphy_init_formats - Initialize formats on all pads 515ec6859b2STodor Tomov * @sd: CSIPHY V4L2 subdevice 516ec6859b2STodor Tomov * @fh: V4L2 subdev file handle 517ec6859b2STodor Tomov * 518ec6859b2STodor Tomov * Initialize all pad formats with default values. 519ec6859b2STodor Tomov * 520ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 521ec6859b2STodor Tomov */ 522ec6859b2STodor Tomov static int csiphy_init_formats(struct v4l2_subdev *sd, 523ec6859b2STodor Tomov struct v4l2_subdev_fh *fh) 524ec6859b2STodor Tomov { 525ec6859b2STodor Tomov struct v4l2_subdev_format format = { 526ec6859b2STodor Tomov .pad = MSM_CSIPHY_PAD_SINK, 527ec6859b2STodor Tomov .which = fh ? V4L2_SUBDEV_FORMAT_TRY : 528ec6859b2STodor Tomov V4L2_SUBDEV_FORMAT_ACTIVE, 529ec6859b2STodor Tomov .format = { 53089936bfbSMartin Dørum .code = MEDIA_BUS_FMT_UYVY8_1X16, 531ec6859b2STodor Tomov .width = 1920, 532ec6859b2STodor Tomov .height = 1080 533ec6859b2STodor Tomov } 534ec6859b2STodor Tomov }; 535ec6859b2STodor Tomov 5360d346d2aSTomi Valkeinen return csiphy_set_format(sd, fh ? fh->state : NULL, &format); 537ec6859b2STodor Tomov } 538ec6859b2STodor Tomov 539*0727615fSBryan O'Donoghue static bool csiphy_match_clock_name(const char *clock_name, const char *format, 540*0727615fSBryan O'Donoghue int index) 541*0727615fSBryan O'Donoghue { 542*0727615fSBryan O'Donoghue char name[16]; /* csiphyXXX_timer\0 */ 543*0727615fSBryan O'Donoghue 544*0727615fSBryan O'Donoghue snprintf(name, sizeof(name), format, index); 545*0727615fSBryan O'Donoghue return !strcmp(clock_name, name); 546*0727615fSBryan O'Donoghue } 547*0727615fSBryan O'Donoghue 548ec6859b2STodor Tomov /* 549ec6859b2STodor Tomov * msm_csiphy_subdev_init - Initialize CSIPHY device structure and resources 550ec6859b2STodor Tomov * @csiphy: CSIPHY device 551ec6859b2STodor Tomov * @res: CSIPHY module resources table 552ec6859b2STodor Tomov * @id: CSIPHY module id 553ec6859b2STodor Tomov * 554ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 555ec6859b2STodor Tomov */ 5569c3e59deSTodor Tomov int msm_csiphy_subdev_init(struct camss *camss, 5579c3e59deSTodor Tomov struct csiphy_device *csiphy, 5581643b787SBryan O'Donoghue const struct camss_subdev_resources *res, u8 id) 559ec6859b2STodor Tomov { 5609c3e59deSTodor Tomov struct device *dev = camss->dev; 561ec6859b2STodor Tomov struct platform_device *pdev = to_platform_device(dev); 562*0727615fSBryan O'Donoghue int i, j, k; 563ec6859b2STodor Tomov int ret; 564ec6859b2STodor Tomov 5659c3e59deSTodor Tomov csiphy->camss = camss; 566ec6859b2STodor Tomov csiphy->id = id; 567ec6859b2STodor Tomov csiphy->cfg.combo_mode = 0; 568c23c7998SBryan O'Donoghue csiphy->ops = res->ops; 569ec6859b2STodor Tomov 5702de3a654SBryan O'Donoghue switch (camss->res->version) { 5712de3a654SBryan O'Donoghue case CAMSS_8x16: 572cba3819dSTodor Tomov csiphy->formats = csiphy_formats_8x16; 573cba3819dSTodor Tomov csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x16); 5742de3a654SBryan O'Donoghue break; 5752de3a654SBryan O'Donoghue case CAMSS_8x96: 5762de3a654SBryan O'Donoghue case CAMSS_660: 577cba3819dSTodor Tomov csiphy->formats = csiphy_formats_8x96; 578cba3819dSTodor Tomov csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96); 5792de3a654SBryan O'Donoghue break; 5802de3a654SBryan O'Donoghue case CAMSS_845: 5812de3a654SBryan O'Donoghue case CAMSS_8250: 5822f8b6719SRobert Foss csiphy->formats = csiphy_formats_sdm845; 5832f8b6719SRobert Foss csiphy->nformats = ARRAY_SIZE(csiphy_formats_sdm845); 5842de3a654SBryan O'Donoghue break; 585cba3819dSTodor Tomov } 586516e8f0fSTodor Tomov 587ec6859b2STodor Tomov /* Memory */ 588ec6859b2STodor Tomov 589414e0a64Sdingsenjie csiphy->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); 5906fe1152aSYang Yingliang if (IS_ERR(csiphy->base)) 591ec6859b2STodor Tomov return PTR_ERR(csiphy->base); 592ec6859b2STodor Tomov 5935900b051SBryan O'Donoghue if (camss->res->version == CAMSS_8x16 || 5945900b051SBryan O'Donoghue camss->res->version == CAMSS_8x96) { 595414e0a64Sdingsenjie csiphy->base_clk_mux = 596414e0a64Sdingsenjie devm_platform_ioremap_resource_byname(pdev, res->reg[1]); 5976fe1152aSYang Yingliang if (IS_ERR(csiphy->base_clk_mux)) 598ec6859b2STodor Tomov return PTR_ERR(csiphy->base_clk_mux); 5992f8b6719SRobert Foss } else { 6002f8b6719SRobert Foss csiphy->base_clk_mux = NULL; 6012f8b6719SRobert Foss } 602ec6859b2STodor Tomov 603ec6859b2STodor Tomov /* Interrupt */ 604ec6859b2STodor Tomov 605b416be3aSLad Prabhakar ret = platform_get_irq_byname(pdev, res->interrupt[0]); 606b416be3aSLad Prabhakar if (ret < 0) 607b416be3aSLad Prabhakar return ret; 608ec6859b2STodor Tomov 609b416be3aSLad Prabhakar csiphy->irq = ret; 610ec6859b2STodor Tomov snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d", 611ec6859b2STodor Tomov dev_name(dev), MSM_CSIPHY_NAME, csiphy->id); 612516e8f0fSTodor Tomov 613516e8f0fSTodor Tomov ret = devm_request_irq(dev, csiphy->irq, csiphy->ops->isr, 61414480e8dSTian Tao IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN, 61514480e8dSTian Tao csiphy->irq_name, csiphy); 616ec6859b2STodor Tomov if (ret < 0) { 617ec6859b2STodor Tomov dev_err(dev, "request_irq failed: %d\n", ret); 618ec6859b2STodor Tomov return ret; 619ec6859b2STodor Tomov } 620ec6859b2STodor Tomov 621ec6859b2STodor Tomov /* Clocks */ 622ec6859b2STodor Tomov 623ec6859b2STodor Tomov csiphy->nclocks = 0; 624ec6859b2STodor Tomov while (res->clock[csiphy->nclocks]) 625ec6859b2STodor Tomov csiphy->nclocks++; 626ec6859b2STodor Tomov 627ec6859b2STodor Tomov csiphy->clock = devm_kcalloc(dev, 628ec6859b2STodor Tomov csiphy->nclocks, sizeof(*csiphy->clock), 629ec6859b2STodor Tomov GFP_KERNEL); 630ec6859b2STodor Tomov if (!csiphy->clock) 631ec6859b2STodor Tomov return -ENOMEM; 632ec6859b2STodor Tomov 6334863b93cSAngeloGioacchino Del Regno csiphy->rate_set = devm_kcalloc(dev, 6344863b93cSAngeloGioacchino Del Regno csiphy->nclocks, 6354863b93cSAngeloGioacchino Del Regno sizeof(*csiphy->rate_set), 6364863b93cSAngeloGioacchino Del Regno GFP_KERNEL); 6374863b93cSAngeloGioacchino Del Regno if (!csiphy->rate_set) 6384863b93cSAngeloGioacchino Del Regno return -ENOMEM; 6394863b93cSAngeloGioacchino Del Regno 640ec6859b2STodor Tomov for (i = 0; i < csiphy->nclocks; i++) { 641ec6859b2STodor Tomov struct camss_clock *clock = &csiphy->clock[i]; 642ec6859b2STodor Tomov 643ec6859b2STodor Tomov clock->clk = devm_clk_get(dev, res->clock[i]); 644ec6859b2STodor Tomov if (IS_ERR(clock->clk)) 645ec6859b2STodor Tomov return PTR_ERR(clock->clk); 646ec6859b2STodor Tomov 647ec6859b2STodor Tomov clock->name = res->clock[i]; 648ec6859b2STodor Tomov 649ec6859b2STodor Tomov clock->nfreqs = 0; 650ec6859b2STodor Tomov while (res->clock_rate[i][clock->nfreqs]) 651ec6859b2STodor Tomov clock->nfreqs++; 652ec6859b2STodor Tomov 653ec6859b2STodor Tomov if (!clock->nfreqs) { 654ec6859b2STodor Tomov clock->freq = NULL; 655ec6859b2STodor Tomov continue; 656ec6859b2STodor Tomov } 657ec6859b2STodor Tomov 658ec6859b2STodor Tomov clock->freq = devm_kcalloc(dev, 659ec6859b2STodor Tomov clock->nfreqs, 660ec6859b2STodor Tomov sizeof(*clock->freq), 661ec6859b2STodor Tomov GFP_KERNEL); 662ec6859b2STodor Tomov if (!clock->freq) 663ec6859b2STodor Tomov return -ENOMEM; 664ec6859b2STodor Tomov 665ec6859b2STodor Tomov for (j = 0; j < clock->nfreqs; j++) 666ec6859b2STodor Tomov clock->freq[j] = res->clock_rate[i][j]; 6674863b93cSAngeloGioacchino Del Regno 668*0727615fSBryan O'Donoghue for (k = 0; k < camss->res->csiphy_num; k++) { 669*0727615fSBryan O'Donoghue csiphy->rate_set[i] = csiphy_match_clock_name(clock->name, 670*0727615fSBryan O'Donoghue "csiphy%d_timer", k); 671*0727615fSBryan O'Donoghue if (csiphy->rate_set[i]) 672*0727615fSBryan O'Donoghue break; 6734863b93cSAngeloGioacchino Del Regno 674*0727615fSBryan O'Donoghue if (camss->res->version == CAMSS_660) { 675*0727615fSBryan O'Donoghue csiphy->rate_set[i] = csiphy_match_clock_name(clock->name, 676*0727615fSBryan O'Donoghue "csi%d_phy", k); 677*0727615fSBryan O'Donoghue if (csiphy->rate_set[i]) 678*0727615fSBryan O'Donoghue break; 679*0727615fSBryan O'Donoghue } 680*0727615fSBryan O'Donoghue } 681ec6859b2STodor Tomov } 682ec6859b2STodor Tomov 683ec6859b2STodor Tomov return 0; 684ec6859b2STodor Tomov } 685ec6859b2STodor Tomov 686ec6859b2STodor Tomov /* 687ec6859b2STodor Tomov * csiphy_link_setup - Setup CSIPHY connections 688ec6859b2STodor Tomov * @entity: Pointer to media entity structure 689ec6859b2STodor Tomov * @local: Pointer to local pad 690ec6859b2STodor Tomov * @remote: Pointer to remote pad 691ec6859b2STodor Tomov * @flags: Link flags 692ec6859b2STodor Tomov * 693ec6859b2STodor Tomov * Rreturn 0 on success 694ec6859b2STodor Tomov */ 695ec6859b2STodor Tomov static int csiphy_link_setup(struct media_entity *entity, 696ec6859b2STodor Tomov const struct media_pad *local, 697ec6859b2STodor Tomov const struct media_pad *remote, u32 flags) 698ec6859b2STodor Tomov { 699ec6859b2STodor Tomov if ((local->flags & MEDIA_PAD_FL_SOURCE) && 700ec6859b2STodor Tomov (flags & MEDIA_LNK_FL_ENABLED)) { 701ec6859b2STodor Tomov struct v4l2_subdev *sd; 702ec6859b2STodor Tomov struct csiphy_device *csiphy; 703ec6859b2STodor Tomov struct csid_device *csid; 704ec6859b2STodor Tomov 705b2e44430SLaurent Pinchart if (media_pad_remote_pad_first(local)) 706ec6859b2STodor Tomov return -EBUSY; 707ec6859b2STodor Tomov 708ec6859b2STodor Tomov sd = media_entity_to_v4l2_subdev(entity); 709ec6859b2STodor Tomov csiphy = v4l2_get_subdevdata(sd); 710ec6859b2STodor Tomov 711ec6859b2STodor Tomov sd = media_entity_to_v4l2_subdev(remote->entity); 712ec6859b2STodor Tomov csid = v4l2_get_subdevdata(sd); 713ec6859b2STodor Tomov 714ec6859b2STodor Tomov csiphy->cfg.csid_id = csid->id; 715ec6859b2STodor Tomov } 716ec6859b2STodor Tomov 717ec6859b2STodor Tomov return 0; 718ec6859b2STodor Tomov } 719ec6859b2STodor Tomov 720ec6859b2STodor Tomov static const struct v4l2_subdev_core_ops csiphy_core_ops = { 721ec6859b2STodor Tomov .s_power = csiphy_set_power, 722ec6859b2STodor Tomov }; 723ec6859b2STodor Tomov 724ec6859b2STodor Tomov static const struct v4l2_subdev_video_ops csiphy_video_ops = { 725ec6859b2STodor Tomov .s_stream = csiphy_set_stream, 726ec6859b2STodor Tomov }; 727ec6859b2STodor Tomov 728ec6859b2STodor Tomov static const struct v4l2_subdev_pad_ops csiphy_pad_ops = { 729ec6859b2STodor Tomov .enum_mbus_code = csiphy_enum_mbus_code, 730ec6859b2STodor Tomov .enum_frame_size = csiphy_enum_frame_size, 731ec6859b2STodor Tomov .get_fmt = csiphy_get_format, 732ec6859b2STodor Tomov .set_fmt = csiphy_set_format, 733ec6859b2STodor Tomov }; 734ec6859b2STodor Tomov 735ec6859b2STodor Tomov static const struct v4l2_subdev_ops csiphy_v4l2_ops = { 736ec6859b2STodor Tomov .core = &csiphy_core_ops, 737ec6859b2STodor Tomov .video = &csiphy_video_ops, 738ec6859b2STodor Tomov .pad = &csiphy_pad_ops, 739ec6859b2STodor Tomov }; 740ec6859b2STodor Tomov 741ec6859b2STodor Tomov static const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops = { 742ec6859b2STodor Tomov .open = csiphy_init_formats, 743ec6859b2STodor Tomov }; 744ec6859b2STodor Tomov 745ec6859b2STodor Tomov static const struct media_entity_operations csiphy_media_ops = { 746ec6859b2STodor Tomov .link_setup = csiphy_link_setup, 747ec6859b2STodor Tomov .link_validate = v4l2_subdev_link_validate, 748ec6859b2STodor Tomov }; 749ec6859b2STodor Tomov 750ec6859b2STodor Tomov /* 751ec6859b2STodor Tomov * msm_csiphy_register_entity - Register subdev node for CSIPHY module 752ec6859b2STodor Tomov * @csiphy: CSIPHY device 753ec6859b2STodor Tomov * @v4l2_dev: V4L2 device 754ec6859b2STodor Tomov * 755ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 756ec6859b2STodor Tomov */ 757ec6859b2STodor Tomov int msm_csiphy_register_entity(struct csiphy_device *csiphy, 758ec6859b2STodor Tomov struct v4l2_device *v4l2_dev) 759ec6859b2STodor Tomov { 760ec6859b2STodor Tomov struct v4l2_subdev *sd = &csiphy->subdev; 761ec6859b2STodor Tomov struct media_pad *pads = csiphy->pads; 7629c3e59deSTodor Tomov struct device *dev = csiphy->camss->dev; 763ec6859b2STodor Tomov int ret; 764ec6859b2STodor Tomov 765ec6859b2STodor Tomov v4l2_subdev_init(sd, &csiphy_v4l2_ops); 766ec6859b2STodor Tomov sd->internal_ops = &csiphy_v4l2_internal_ops; 767ec6859b2STodor Tomov sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 768ec6859b2STodor Tomov snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", 769ec6859b2STodor Tomov MSM_CSIPHY_NAME, csiphy->id); 770ec6859b2STodor Tomov v4l2_set_subdevdata(sd, csiphy); 771ec6859b2STodor Tomov 772ec6859b2STodor Tomov ret = csiphy_init_formats(sd, NULL); 773ec6859b2STodor Tomov if (ret < 0) { 774ec6859b2STodor Tomov dev_err(dev, "Failed to init format: %d\n", ret); 775ec6859b2STodor Tomov return ret; 776ec6859b2STodor Tomov } 777ec6859b2STodor Tomov 778ec6859b2STodor Tomov pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 779ec6859b2STodor Tomov pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; 780ec6859b2STodor Tomov 78150795910SAndrey Konovalov sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 782ec6859b2STodor Tomov sd->entity.ops = &csiphy_media_ops; 783ec6859b2STodor Tomov ret = media_entity_pads_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads); 784ec6859b2STodor Tomov if (ret < 0) { 785ec6859b2STodor Tomov dev_err(dev, "Failed to init media entity: %d\n", ret); 786ec6859b2STodor Tomov return ret; 787ec6859b2STodor Tomov } 788ec6859b2STodor Tomov 789ec6859b2STodor Tomov ret = v4l2_device_register_subdev(v4l2_dev, sd); 790ec6859b2STodor Tomov if (ret < 0) { 791ec6859b2STodor Tomov dev_err(dev, "Failed to register subdev: %d\n", ret); 792ec6859b2STodor Tomov media_entity_cleanup(&sd->entity); 793ec6859b2STodor Tomov } 794ec6859b2STodor Tomov 795ec6859b2STodor Tomov return ret; 796ec6859b2STodor Tomov } 797ec6859b2STodor Tomov 798ec6859b2STodor Tomov /* 799ec6859b2STodor Tomov * msm_csiphy_unregister_entity - Unregister CSIPHY module subdev node 800ec6859b2STodor Tomov * @csiphy: CSIPHY device 801ec6859b2STodor Tomov */ 802ec6859b2STodor Tomov void msm_csiphy_unregister_entity(struct csiphy_device *csiphy) 803ec6859b2STodor Tomov { 804ec6859b2STodor Tomov v4l2_device_unregister_subdev(&csiphy->subdev); 805ec6859b2STodor Tomov media_entity_cleanup(&csiphy->subdev.entity); 806ec6859b2STodor Tomov } 807