18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * camss-csiphy.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Qualcomm MSM Camera Subsystem - CSIPHY Module 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. 88c2ecf20Sopenharmony_ci * Copyright (C) 2016-2018 Linaro Ltd. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 188c2ecf20Sopenharmony_ci#include <media/media-entity.h> 198c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 208c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "camss-csiphy.h" 238c2ecf20Sopenharmony_ci#include "camss.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define MSM_CSIPHY_NAME "msm_csiphy" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct csiphy_format { 288c2ecf20Sopenharmony_ci u32 code; 298c2ecf20Sopenharmony_ci u8 bpp; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const struct csiphy_format csiphy_formats_8x16[] = { 338c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, 348c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, 358c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, 368c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, 378c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, 388c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, 398c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, 408c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, 418c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, 428c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, 438c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, 448c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 458c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, 468c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, 478c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, 488c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 498c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_Y10_1X10, 10 }, 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic const struct csiphy_format csiphy_formats_8x96[] = { 538c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, 548c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, 558c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, 568c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, 578c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, 588c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, 598c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, 608c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, 618c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, 628c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, 638c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, 648c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 658c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, 668c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, 678c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, 688c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 698c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, 708c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, 718c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, 728c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, 738c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_Y10_1X10, 10 }, 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * csiphy_get_bpp - map media bus format to bits per pixel 788c2ecf20Sopenharmony_ci * @formats: supported media bus formats array 798c2ecf20Sopenharmony_ci * @nformats: size of @formats array 808c2ecf20Sopenharmony_ci * @code: media bus format code 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * Return number of bits per pixel 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistatic u8 csiphy_get_bpp(const struct csiphy_format *formats, 858c2ecf20Sopenharmony_ci unsigned int nformats, u32 code) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci unsigned int i; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci for (i = 0; i < nformats; i++) 908c2ecf20Sopenharmony_ci if (code == formats[i].code) 918c2ecf20Sopenharmony_ci return formats[i].bpp; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci WARN(1, "Unknown format\n"); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return formats[0].bpp; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * csiphy_set_clock_rates - Calculate and set clock rates on CSIPHY module 1008c2ecf20Sopenharmony_ci * @csiphy: CSIPHY device 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_cistatic int csiphy_set_clock_rates(struct csiphy_device *csiphy) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct device *dev = csiphy->camss->dev; 1058c2ecf20Sopenharmony_ci u32 pixel_clock; 1068c2ecf20Sopenharmony_ci int i, j; 1078c2ecf20Sopenharmony_ci int ret; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock); 1108c2ecf20Sopenharmony_ci if (ret) 1118c2ecf20Sopenharmony_ci pixel_clock = 0; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci for (i = 0; i < csiphy->nclocks; i++) { 1148c2ecf20Sopenharmony_ci struct camss_clock *clock = &csiphy->clock[i]; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!strcmp(clock->name, "csiphy0_timer") || 1178c2ecf20Sopenharmony_ci !strcmp(clock->name, "csiphy1_timer") || 1188c2ecf20Sopenharmony_ci !strcmp(clock->name, "csiphy2_timer")) { 1198c2ecf20Sopenharmony_ci u8 bpp = csiphy_get_bpp(csiphy->formats, 1208c2ecf20Sopenharmony_ci csiphy->nformats, 1218c2ecf20Sopenharmony_ci csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); 1228c2ecf20Sopenharmony_ci u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; 1238c2ecf20Sopenharmony_ci u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4); 1248c2ecf20Sopenharmony_ci long round_rate; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci camss_add_clock_margin(&min_rate); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci for (j = 0; j < clock->nfreqs; j++) 1298c2ecf20Sopenharmony_ci if (min_rate < clock->freq[j]) 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (j == clock->nfreqs) { 1338c2ecf20Sopenharmony_ci dev_err(dev, 1348c2ecf20Sopenharmony_ci "Pixel clock is too high for CSIPHY\n"); 1358c2ecf20Sopenharmony_ci return -EINVAL; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* if sensor pixel clock is not available */ 1398c2ecf20Sopenharmony_ci /* set highest possible CSIPHY clock rate */ 1408c2ecf20Sopenharmony_ci if (min_rate == 0) 1418c2ecf20Sopenharmony_ci j = clock->nfreqs - 1; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci round_rate = clk_round_rate(clock->clk, clock->freq[j]); 1448c2ecf20Sopenharmony_ci if (round_rate < 0) { 1458c2ecf20Sopenharmony_ci dev_err(dev, "clk round rate failed: %ld\n", 1468c2ecf20Sopenharmony_ci round_rate); 1478c2ecf20Sopenharmony_ci return -EINVAL; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci csiphy->timer_clk_rate = round_rate; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci ret = clk_set_rate(clock->clk, csiphy->timer_clk_rate); 1538c2ecf20Sopenharmony_ci if (ret < 0) { 1548c2ecf20Sopenharmony_ci dev_err(dev, "clk set rate failed: %d\n", ret); 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* 1648c2ecf20Sopenharmony_ci * csiphy_set_power - Power on/off CSIPHY module 1658c2ecf20Sopenharmony_ci * @sd: CSIPHY V4L2 subdevice 1668c2ecf20Sopenharmony_ci * @on: Requested power state 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_cistatic int csiphy_set_power(struct v4l2_subdev *sd, int on) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); 1738c2ecf20Sopenharmony_ci struct device *dev = csiphy->camss->dev; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (on) { 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev); 1798c2ecf20Sopenharmony_ci if (ret < 0) { 1808c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 1818c2ecf20Sopenharmony_ci return ret; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ret = csiphy_set_clock_rates(csiphy); 1858c2ecf20Sopenharmony_ci if (ret < 0) { 1868c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev); 1918c2ecf20Sopenharmony_ci if (ret < 0) { 1928c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci enable_irq(csiphy->irq); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci csiphy->ops->reset(csiphy); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci csiphy->ops->hw_version_read(csiphy, dev); 2018c2ecf20Sopenharmony_ci } else { 2028c2ecf20Sopenharmony_ci disable_irq(csiphy->irq); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci camss_disable_clocks(csiphy->nclocks, csiphy->clock); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* 2138c2ecf20Sopenharmony_ci * csiphy_get_lane_mask - Calculate CSI2 lane mask configuration parameter 2148c2ecf20Sopenharmony_ci * @lane_cfg - CSI2 lane configuration 2158c2ecf20Sopenharmony_ci * 2168c2ecf20Sopenharmony_ci * Return lane mask 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_cistatic u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci u8 lane_mask; 2218c2ecf20Sopenharmony_ci int i; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci lane_mask = 1 << lane_cfg->clk.pos; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci for (i = 0; i < lane_cfg->num_data; i++) 2268c2ecf20Sopenharmony_ci lane_mask |= 1 << lane_cfg->data[i].pos; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return lane_mask; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/* 2328c2ecf20Sopenharmony_ci * csiphy_stream_on - Enable streaming on CSIPHY module 2338c2ecf20Sopenharmony_ci * @csiphy: CSIPHY device 2348c2ecf20Sopenharmony_ci * 2358c2ecf20Sopenharmony_ci * Helper function to enable streaming on CSIPHY module. 2368c2ecf20Sopenharmony_ci * Main configuration of CSIPHY module is also done here. 2378c2ecf20Sopenharmony_ci * 2388c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_cistatic int csiphy_stream_on(struct csiphy_device *csiphy) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct csiphy_config *cfg = &csiphy->cfg; 2438c2ecf20Sopenharmony_ci u32 pixel_clock; 2448c2ecf20Sopenharmony_ci u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg); 2458c2ecf20Sopenharmony_ci u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, 2468c2ecf20Sopenharmony_ci csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); 2478c2ecf20Sopenharmony_ci u8 val; 2488c2ecf20Sopenharmony_ci int ret; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock); 2518c2ecf20Sopenharmony_ci if (ret) { 2528c2ecf20Sopenharmony_ci dev_err(csiphy->camss->dev, 2538c2ecf20Sopenharmony_ci "Cannot get CSI2 transmitter's pixel clock\n"); 2548c2ecf20Sopenharmony_ci return -EINVAL; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci if (!pixel_clock) { 2578c2ecf20Sopenharmony_ci dev_err(csiphy->camss->dev, 2588c2ecf20Sopenharmony_ci "Got pixel clock == 0, cannot continue\n"); 2598c2ecf20Sopenharmony_ci return -EINVAL; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci val = readl_relaxed(csiphy->base_clk_mux); 2638c2ecf20Sopenharmony_ci if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) { 2648c2ecf20Sopenharmony_ci val &= ~0xf0; 2658c2ecf20Sopenharmony_ci val |= cfg->csid_id << 4; 2668c2ecf20Sopenharmony_ci } else { 2678c2ecf20Sopenharmony_ci val &= ~0xf; 2688c2ecf20Sopenharmony_ci val |= cfg->csid_id; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci writel_relaxed(val, csiphy->base_clk_mux); 2718c2ecf20Sopenharmony_ci wmb(); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci csiphy->ops->lanes_enable(csiphy, cfg, pixel_clock, bpp, lane_mask); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/* 2798c2ecf20Sopenharmony_ci * csiphy_stream_off - Disable streaming on CSIPHY module 2808c2ecf20Sopenharmony_ci * @csiphy: CSIPHY device 2818c2ecf20Sopenharmony_ci * 2828c2ecf20Sopenharmony_ci * Helper function to disable streaming on CSIPHY module 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_cistatic void csiphy_stream_off(struct csiphy_device *csiphy) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci csiphy->ops->lanes_disable(csiphy, &csiphy->cfg); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* 2918c2ecf20Sopenharmony_ci * csiphy_set_stream - Enable/disable streaming on CSIPHY module 2928c2ecf20Sopenharmony_ci * @sd: CSIPHY V4L2 subdevice 2938c2ecf20Sopenharmony_ci * @enable: Requested streaming state 2948c2ecf20Sopenharmony_ci * 2958c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_cistatic int csiphy_set_stream(struct v4l2_subdev *sd, int enable) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); 3008c2ecf20Sopenharmony_ci int ret = 0; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (enable) 3038c2ecf20Sopenharmony_ci ret = csiphy_stream_on(csiphy); 3048c2ecf20Sopenharmony_ci else 3058c2ecf20Sopenharmony_ci csiphy_stream_off(csiphy); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return ret; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* 3118c2ecf20Sopenharmony_ci * __csiphy_get_format - Get pointer to format structure 3128c2ecf20Sopenharmony_ci * @csiphy: CSIPHY device 3138c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 3148c2ecf20Sopenharmony_ci * @pad: pad from which format is requested 3158c2ecf20Sopenharmony_ci * @which: TRY or ACTIVE format 3168c2ecf20Sopenharmony_ci * 3178c2ecf20Sopenharmony_ci * Return pointer to TRY or ACTIVE format structure 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_cistatic struct v4l2_mbus_framefmt * 3208c2ecf20Sopenharmony_ci__csiphy_get_format(struct csiphy_device *csiphy, 3218c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 3228c2ecf20Sopenharmony_ci unsigned int pad, 3238c2ecf20Sopenharmony_ci enum v4l2_subdev_format_whence which) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 3268c2ecf20Sopenharmony_ci return v4l2_subdev_get_try_format(&csiphy->subdev, cfg, pad); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return &csiphy->fmt[pad]; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/* 3328c2ecf20Sopenharmony_ci * csiphy_try_format - Handle try format by pad subdev method 3338c2ecf20Sopenharmony_ci * @csiphy: CSIPHY device 3348c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 3358c2ecf20Sopenharmony_ci * @pad: pad on which format is requested 3368c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 format structure 3378c2ecf20Sopenharmony_ci * @which: wanted subdev format 3388c2ecf20Sopenharmony_ci */ 3398c2ecf20Sopenharmony_cistatic void csiphy_try_format(struct csiphy_device *csiphy, 3408c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 3418c2ecf20Sopenharmony_ci unsigned int pad, 3428c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt, 3438c2ecf20Sopenharmony_ci enum v4l2_subdev_format_whence which) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci unsigned int i; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci switch (pad) { 3488c2ecf20Sopenharmony_ci case MSM_CSIPHY_PAD_SINK: 3498c2ecf20Sopenharmony_ci /* Set format on sink pad */ 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci for (i = 0; i < csiphy->nformats; i++) 3528c2ecf20Sopenharmony_ci if (fmt->code == csiphy->formats[i].code) 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* If not found, use UYVY as default */ 3568c2ecf20Sopenharmony_ci if (i >= csiphy->nformats) 3578c2ecf20Sopenharmony_ci fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci fmt->width = clamp_t(u32, fmt->width, 1, 8191); 3608c2ecf20Sopenharmony_ci fmt->height = clamp_t(u32, fmt->height, 1, 8191); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci fmt->field = V4L2_FIELD_NONE; 3638c2ecf20Sopenharmony_ci fmt->colorspace = V4L2_COLORSPACE_SRGB; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci case MSM_CSIPHY_PAD_SRC: 3688c2ecf20Sopenharmony_ci /* Set and return a format same as sink pad */ 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci *fmt = *__csiphy_get_format(csiphy, cfg, MSM_CSID_PAD_SINK, 3718c2ecf20Sopenharmony_ci which); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* 3788c2ecf20Sopenharmony_ci * csiphy_enum_mbus_code - Handle pixel format enumeration 3798c2ecf20Sopenharmony_ci * @sd: CSIPHY V4L2 subdevice 3808c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 3818c2ecf20Sopenharmony_ci * @code: pointer to v4l2_subdev_mbus_code_enum structure 3828c2ecf20Sopenharmony_ci * return -EINVAL or zero on success 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_cistatic int csiphy_enum_mbus_code(struct v4l2_subdev *sd, 3858c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 3868c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); 3898c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (code->pad == MSM_CSIPHY_PAD_SINK) { 3928c2ecf20Sopenharmony_ci if (code->index >= csiphy->nformats) 3938c2ecf20Sopenharmony_ci return -EINVAL; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci code->code = csiphy->formats[code->index].code; 3968c2ecf20Sopenharmony_ci } else { 3978c2ecf20Sopenharmony_ci if (code->index > 0) 3988c2ecf20Sopenharmony_ci return -EINVAL; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SINK, 4018c2ecf20Sopenharmony_ci code->which); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci code->code = format->code; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/* 4108c2ecf20Sopenharmony_ci * csiphy_enum_frame_size - Handle frame size enumeration 4118c2ecf20Sopenharmony_ci * @sd: CSIPHY V4L2 subdevice 4128c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 4138c2ecf20Sopenharmony_ci * @fse: pointer to v4l2_subdev_frame_size_enum structure 4148c2ecf20Sopenharmony_ci * return -EINVAL or zero on success 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_cistatic int csiphy_enum_frame_size(struct v4l2_subdev *sd, 4178c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 4188c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); 4218c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt format; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (fse->index != 0) 4248c2ecf20Sopenharmony_ci return -EINVAL; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci format.code = fse->code; 4278c2ecf20Sopenharmony_ci format.width = 1; 4288c2ecf20Sopenharmony_ci format.height = 1; 4298c2ecf20Sopenharmony_ci csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which); 4308c2ecf20Sopenharmony_ci fse->min_width = format.width; 4318c2ecf20Sopenharmony_ci fse->min_height = format.height; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (format.code != fse->code) 4348c2ecf20Sopenharmony_ci return -EINVAL; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci format.code = fse->code; 4378c2ecf20Sopenharmony_ci format.width = -1; 4388c2ecf20Sopenharmony_ci format.height = -1; 4398c2ecf20Sopenharmony_ci csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which); 4408c2ecf20Sopenharmony_ci fse->max_width = format.width; 4418c2ecf20Sopenharmony_ci fse->max_height = format.height; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return 0; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci/* 4478c2ecf20Sopenharmony_ci * csiphy_get_format - Handle get format by pads subdev method 4488c2ecf20Sopenharmony_ci * @sd: CSIPHY V4L2 subdevice 4498c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 4508c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure 4518c2ecf20Sopenharmony_ci * 4528c2ecf20Sopenharmony_ci * Return -EINVAL or zero on success 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistatic int csiphy_get_format(struct v4l2_subdev *sd, 4558c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 4568c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); 4598c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which); 4628c2ecf20Sopenharmony_ci if (format == NULL) 4638c2ecf20Sopenharmony_ci return -EINVAL; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci fmt->format = *format; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/* 4718c2ecf20Sopenharmony_ci * csiphy_set_format - Handle set format by pads subdev method 4728c2ecf20Sopenharmony_ci * @sd: CSIPHY V4L2 subdevice 4738c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 4748c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure 4758c2ecf20Sopenharmony_ci * 4768c2ecf20Sopenharmony_ci * Return -EINVAL or zero on success 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_cistatic int csiphy_set_format(struct v4l2_subdev *sd, 4798c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 4808c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); 4838c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which); 4868c2ecf20Sopenharmony_ci if (format == NULL) 4878c2ecf20Sopenharmony_ci return -EINVAL; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci csiphy_try_format(csiphy, cfg, fmt->pad, &fmt->format, fmt->which); 4908c2ecf20Sopenharmony_ci *format = fmt->format; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* Propagate the format from sink to source */ 4938c2ecf20Sopenharmony_ci if (fmt->pad == MSM_CSIPHY_PAD_SINK) { 4948c2ecf20Sopenharmony_ci format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC, 4958c2ecf20Sopenharmony_ci fmt->which); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci *format = fmt->format; 4988c2ecf20Sopenharmony_ci csiphy_try_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC, format, 4998c2ecf20Sopenharmony_ci fmt->which); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/* 5068c2ecf20Sopenharmony_ci * csiphy_init_formats - Initialize formats on all pads 5078c2ecf20Sopenharmony_ci * @sd: CSIPHY V4L2 subdevice 5088c2ecf20Sopenharmony_ci * @fh: V4L2 subdev file handle 5098c2ecf20Sopenharmony_ci * 5108c2ecf20Sopenharmony_ci * Initialize all pad formats with default values. 5118c2ecf20Sopenharmony_ci * 5128c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_cistatic int csiphy_init_formats(struct v4l2_subdev *sd, 5158c2ecf20Sopenharmony_ci struct v4l2_subdev_fh *fh) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct v4l2_subdev_format format = { 5188c2ecf20Sopenharmony_ci .pad = MSM_CSIPHY_PAD_SINK, 5198c2ecf20Sopenharmony_ci .which = fh ? V4L2_SUBDEV_FORMAT_TRY : 5208c2ecf20Sopenharmony_ci V4L2_SUBDEV_FORMAT_ACTIVE, 5218c2ecf20Sopenharmony_ci .format = { 5228c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_UYVY8_2X8, 5238c2ecf20Sopenharmony_ci .width = 1920, 5248c2ecf20Sopenharmony_ci .height = 1080 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci }; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return csiphy_set_format(sd, fh ? fh->pad : NULL, &format); 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci/* 5328c2ecf20Sopenharmony_ci * msm_csiphy_subdev_init - Initialize CSIPHY device structure and resources 5338c2ecf20Sopenharmony_ci * @csiphy: CSIPHY device 5348c2ecf20Sopenharmony_ci * @res: CSIPHY module resources table 5358c2ecf20Sopenharmony_ci * @id: CSIPHY module id 5368c2ecf20Sopenharmony_ci * 5378c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ciint msm_csiphy_subdev_init(struct camss *camss, 5408c2ecf20Sopenharmony_ci struct csiphy_device *csiphy, 5418c2ecf20Sopenharmony_ci const struct resources *res, u8 id) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct device *dev = camss->dev; 5448c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 5458c2ecf20Sopenharmony_ci struct resource *r; 5468c2ecf20Sopenharmony_ci int i, j; 5478c2ecf20Sopenharmony_ci int ret; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci csiphy->camss = camss; 5508c2ecf20Sopenharmony_ci csiphy->id = id; 5518c2ecf20Sopenharmony_ci csiphy->cfg.combo_mode = 0; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (camss->version == CAMSS_8x16) { 5548c2ecf20Sopenharmony_ci csiphy->ops = &csiphy_ops_2ph_1_0; 5558c2ecf20Sopenharmony_ci csiphy->formats = csiphy_formats_8x16; 5568c2ecf20Sopenharmony_ci csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x16); 5578c2ecf20Sopenharmony_ci } else if (camss->version == CAMSS_8x96) { 5588c2ecf20Sopenharmony_ci csiphy->ops = &csiphy_ops_3ph_1_0; 5598c2ecf20Sopenharmony_ci csiphy->formats = csiphy_formats_8x96; 5608c2ecf20Sopenharmony_ci csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96); 5618c2ecf20Sopenharmony_ci } else { 5628c2ecf20Sopenharmony_ci return -EINVAL; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Memory */ 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); 5688c2ecf20Sopenharmony_ci csiphy->base = devm_ioremap_resource(dev, r); 5698c2ecf20Sopenharmony_ci if (IS_ERR(csiphy->base)) { 5708c2ecf20Sopenharmony_ci dev_err(dev, "could not map memory\n"); 5718c2ecf20Sopenharmony_ci return PTR_ERR(csiphy->base); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]); 5758c2ecf20Sopenharmony_ci csiphy->base_clk_mux = devm_ioremap_resource(dev, r); 5768c2ecf20Sopenharmony_ci if (IS_ERR(csiphy->base_clk_mux)) { 5778c2ecf20Sopenharmony_ci dev_err(dev, "could not map memory\n"); 5788c2ecf20Sopenharmony_ci return PTR_ERR(csiphy->base_clk_mux); 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* Interrupt */ 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, 5848c2ecf20Sopenharmony_ci res->interrupt[0]); 5858c2ecf20Sopenharmony_ci if (!r) { 5868c2ecf20Sopenharmony_ci dev_err(dev, "missing IRQ\n"); 5878c2ecf20Sopenharmony_ci return -EINVAL; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci csiphy->irq = r->start; 5918c2ecf20Sopenharmony_ci snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d", 5928c2ecf20Sopenharmony_ci dev_name(dev), MSM_CSIPHY_NAME, csiphy->id); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, csiphy->irq, csiphy->ops->isr, 5958c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING, csiphy->irq_name, csiphy); 5968c2ecf20Sopenharmony_ci if (ret < 0) { 5978c2ecf20Sopenharmony_ci dev_err(dev, "request_irq failed: %d\n", ret); 5988c2ecf20Sopenharmony_ci return ret; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci disable_irq(csiphy->irq); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* Clocks */ 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci csiphy->nclocks = 0; 6068c2ecf20Sopenharmony_ci while (res->clock[csiphy->nclocks]) 6078c2ecf20Sopenharmony_ci csiphy->nclocks++; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci csiphy->clock = devm_kcalloc(dev, 6108c2ecf20Sopenharmony_ci csiphy->nclocks, sizeof(*csiphy->clock), 6118c2ecf20Sopenharmony_ci GFP_KERNEL); 6128c2ecf20Sopenharmony_ci if (!csiphy->clock) 6138c2ecf20Sopenharmony_ci return -ENOMEM; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci for (i = 0; i < csiphy->nclocks; i++) { 6168c2ecf20Sopenharmony_ci struct camss_clock *clock = &csiphy->clock[i]; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci clock->clk = devm_clk_get(dev, res->clock[i]); 6198c2ecf20Sopenharmony_ci if (IS_ERR(clock->clk)) 6208c2ecf20Sopenharmony_ci return PTR_ERR(clock->clk); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci clock->name = res->clock[i]; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci clock->nfreqs = 0; 6258c2ecf20Sopenharmony_ci while (res->clock_rate[i][clock->nfreqs]) 6268c2ecf20Sopenharmony_ci clock->nfreqs++; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (!clock->nfreqs) { 6298c2ecf20Sopenharmony_ci clock->freq = NULL; 6308c2ecf20Sopenharmony_ci continue; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci clock->freq = devm_kcalloc(dev, 6348c2ecf20Sopenharmony_ci clock->nfreqs, 6358c2ecf20Sopenharmony_ci sizeof(*clock->freq), 6368c2ecf20Sopenharmony_ci GFP_KERNEL); 6378c2ecf20Sopenharmony_ci if (!clock->freq) 6388c2ecf20Sopenharmony_ci return -ENOMEM; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci for (j = 0; j < clock->nfreqs; j++) 6418c2ecf20Sopenharmony_ci clock->freq[j] = res->clock_rate[i][j]; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci/* 6488c2ecf20Sopenharmony_ci * csiphy_link_setup - Setup CSIPHY connections 6498c2ecf20Sopenharmony_ci * @entity: Pointer to media entity structure 6508c2ecf20Sopenharmony_ci * @local: Pointer to local pad 6518c2ecf20Sopenharmony_ci * @remote: Pointer to remote pad 6528c2ecf20Sopenharmony_ci * @flags: Link flags 6538c2ecf20Sopenharmony_ci * 6548c2ecf20Sopenharmony_ci * Rreturn 0 on success 6558c2ecf20Sopenharmony_ci */ 6568c2ecf20Sopenharmony_cistatic int csiphy_link_setup(struct media_entity *entity, 6578c2ecf20Sopenharmony_ci const struct media_pad *local, 6588c2ecf20Sopenharmony_ci const struct media_pad *remote, u32 flags) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci if ((local->flags & MEDIA_PAD_FL_SOURCE) && 6618c2ecf20Sopenharmony_ci (flags & MEDIA_LNK_FL_ENABLED)) { 6628c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 6638c2ecf20Sopenharmony_ci struct csiphy_device *csiphy; 6648c2ecf20Sopenharmony_ci struct csid_device *csid; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (media_entity_remote_pad(local)) 6678c2ecf20Sopenharmony_ci return -EBUSY; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci sd = media_entity_to_v4l2_subdev(entity); 6708c2ecf20Sopenharmony_ci csiphy = v4l2_get_subdevdata(sd); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci sd = media_entity_to_v4l2_subdev(remote->entity); 6738c2ecf20Sopenharmony_ci csid = v4l2_get_subdevdata(sd); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci csiphy->cfg.csid_id = csid->id; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops csiphy_core_ops = { 6828c2ecf20Sopenharmony_ci .s_power = csiphy_set_power, 6838c2ecf20Sopenharmony_ci}; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops csiphy_video_ops = { 6868c2ecf20Sopenharmony_ci .s_stream = csiphy_set_stream, 6878c2ecf20Sopenharmony_ci}; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops csiphy_pad_ops = { 6908c2ecf20Sopenharmony_ci .enum_mbus_code = csiphy_enum_mbus_code, 6918c2ecf20Sopenharmony_ci .enum_frame_size = csiphy_enum_frame_size, 6928c2ecf20Sopenharmony_ci .get_fmt = csiphy_get_format, 6938c2ecf20Sopenharmony_ci .set_fmt = csiphy_set_format, 6948c2ecf20Sopenharmony_ci}; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops csiphy_v4l2_ops = { 6978c2ecf20Sopenharmony_ci .core = &csiphy_core_ops, 6988c2ecf20Sopenharmony_ci .video = &csiphy_video_ops, 6998c2ecf20Sopenharmony_ci .pad = &csiphy_pad_ops, 7008c2ecf20Sopenharmony_ci}; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops = { 7038c2ecf20Sopenharmony_ci .open = csiphy_init_formats, 7048c2ecf20Sopenharmony_ci}; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic const struct media_entity_operations csiphy_media_ops = { 7078c2ecf20Sopenharmony_ci .link_setup = csiphy_link_setup, 7088c2ecf20Sopenharmony_ci .link_validate = v4l2_subdev_link_validate, 7098c2ecf20Sopenharmony_ci}; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci/* 7128c2ecf20Sopenharmony_ci * msm_csiphy_register_entity - Register subdev node for CSIPHY module 7138c2ecf20Sopenharmony_ci * @csiphy: CSIPHY device 7148c2ecf20Sopenharmony_ci * @v4l2_dev: V4L2 device 7158c2ecf20Sopenharmony_ci * 7168c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_ciint msm_csiphy_register_entity(struct csiphy_device *csiphy, 7198c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &csiphy->subdev; 7228c2ecf20Sopenharmony_ci struct media_pad *pads = csiphy->pads; 7238c2ecf20Sopenharmony_ci struct device *dev = csiphy->camss->dev; 7248c2ecf20Sopenharmony_ci int ret; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci v4l2_subdev_init(sd, &csiphy_v4l2_ops); 7278c2ecf20Sopenharmony_ci sd->internal_ops = &csiphy_v4l2_internal_ops; 7288c2ecf20Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 7298c2ecf20Sopenharmony_ci snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", 7308c2ecf20Sopenharmony_ci MSM_CSIPHY_NAME, csiphy->id); 7318c2ecf20Sopenharmony_ci v4l2_set_subdevdata(sd, csiphy); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci ret = csiphy_init_formats(sd, NULL); 7348c2ecf20Sopenharmony_ci if (ret < 0) { 7358c2ecf20Sopenharmony_ci dev_err(dev, "Failed to init format: %d\n", ret); 7368c2ecf20Sopenharmony_ci return ret; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 7408c2ecf20Sopenharmony_ci pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 7438c2ecf20Sopenharmony_ci sd->entity.ops = &csiphy_media_ops; 7448c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads); 7458c2ecf20Sopenharmony_ci if (ret < 0) { 7468c2ecf20Sopenharmony_ci dev_err(dev, "Failed to init media entity: %d\n", ret); 7478c2ecf20Sopenharmony_ci return ret; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci ret = v4l2_device_register_subdev(v4l2_dev, sd); 7518c2ecf20Sopenharmony_ci if (ret < 0) { 7528c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register subdev: %d\n", ret); 7538c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci return ret; 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci/* 7608c2ecf20Sopenharmony_ci * msm_csiphy_unregister_entity - Unregister CSIPHY module subdev node 7618c2ecf20Sopenharmony_ci * @csiphy: CSIPHY device 7628c2ecf20Sopenharmony_ci */ 7638c2ecf20Sopenharmony_civoid msm_csiphy_unregister_entity(struct csiphy_device *csiphy) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(&csiphy->subdev); 7668c2ecf20Sopenharmony_ci media_entity_cleanup(&csiphy->subdev.entity); 7678c2ecf20Sopenharmony_ci} 768