1b873663bSTodor Tomov // SPDX-License-Identifier: GPL-2.0 2ec6859b2STodor Tomov /* 3ec6859b2STodor Tomov * camss.c 4ec6859b2STodor Tomov * 5ec6859b2STodor Tomov * Qualcomm MSM Camera Subsystem - Core 6ec6859b2STodor Tomov * 7ec6859b2STodor Tomov * Copyright (c) 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/media-bus-format.h> 12ec6859b2STodor Tomov #include <linux/media.h> 13ec6859b2STodor Tomov #include <linux/module.h> 14ec6859b2STodor Tomov #include <linux/platform_device.h> 15ec6859b2STodor Tomov #include <linux/of.h> 16ec6859b2STodor Tomov #include <linux/of_graph.h> 1702afa816STodor Tomov #include <linux/pm_runtime.h> 1802afa816STodor Tomov #include <linux/pm_domain.h> 19ec6859b2STodor Tomov #include <linux/slab.h> 20ec6859b2STodor Tomov #include <linux/videodev2.h> 21ec6859b2STodor Tomov 22ec6859b2STodor Tomov #include <media/media-device.h> 23ec6859b2STodor Tomov #include <media/v4l2-async.h> 24ec6859b2STodor Tomov #include <media/v4l2-device.h> 25ec6859b2STodor Tomov #include <media/v4l2-mc.h> 26ec6859b2STodor Tomov #include <media/v4l2-fwnode.h> 27ec6859b2STodor Tomov 28ec6859b2STodor Tomov #include "camss.h" 29ec6859b2STodor Tomov 30ec6859b2STodor Tomov #define CAMSS_CLOCK_MARGIN_NUMERATOR 105 31ec6859b2STodor Tomov #define CAMSS_CLOCK_MARGIN_DENOMINATOR 100 32ec6859b2STodor Tomov 339c3e59deSTodor Tomov static const struct resources csiphy_res_8x16[] = { 34ec6859b2STodor Tomov /* CSIPHY0 */ 35ec6859b2STodor Tomov { 36ec6859b2STodor Tomov .regulator = { NULL }, 3709a94865STodor Tomov .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" }, 38ec6859b2STodor Tomov .clock_rate = { { 0 }, 39ec6859b2STodor Tomov { 0 }, 40ec6859b2STodor Tomov { 0 }, 41ec6859b2STodor Tomov { 100000000, 200000000 } }, 42ec6859b2STodor Tomov .reg = { "csiphy0", "csiphy0_clk_mux" }, 43ec6859b2STodor Tomov .interrupt = { "csiphy0" } 44ec6859b2STodor Tomov }, 45ec6859b2STodor Tomov 46ec6859b2STodor Tomov /* CSIPHY1 */ 47ec6859b2STodor Tomov { 48ec6859b2STodor Tomov .regulator = { NULL }, 4909a94865STodor Tomov .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" }, 50ec6859b2STodor Tomov .clock_rate = { { 0 }, 51ec6859b2STodor Tomov { 0 }, 52ec6859b2STodor Tomov { 0 }, 53ec6859b2STodor Tomov { 100000000, 200000000 } }, 54ec6859b2STodor Tomov .reg = { "csiphy1", "csiphy1_clk_mux" }, 55ec6859b2STodor Tomov .interrupt = { "csiphy1" } 56ec6859b2STodor Tomov } 57ec6859b2STodor Tomov }; 58ec6859b2STodor Tomov 599c3e59deSTodor Tomov static const struct resources csid_res_8x16[] = { 60ec6859b2STodor Tomov /* CSID0 */ 61ec6859b2STodor Tomov { 62ec6859b2STodor Tomov .regulator = { "vdda" }, 6309a94865STodor Tomov .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", 64ec6859b2STodor Tomov "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, 65ec6859b2STodor Tomov .clock_rate = { { 0 }, 66ec6859b2STodor Tomov { 0 }, 67ec6859b2STodor Tomov { 0 }, 68ec6859b2STodor Tomov { 0 }, 69ec6859b2STodor Tomov { 100000000, 200000000 }, 70ec6859b2STodor Tomov { 0 }, 71ec6859b2STodor Tomov { 0 }, 72ec6859b2STodor Tomov { 0 } }, 73ec6859b2STodor Tomov .reg = { "csid0" }, 74ec6859b2STodor Tomov .interrupt = { "csid0" } 75ec6859b2STodor Tomov }, 76ec6859b2STodor Tomov 77ec6859b2STodor Tomov /* CSID1 */ 78ec6859b2STodor Tomov { 79ec6859b2STodor Tomov .regulator = { "vdda" }, 8009a94865STodor Tomov .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", 81ec6859b2STodor Tomov "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, 82ec6859b2STodor Tomov .clock_rate = { { 0 }, 83ec6859b2STodor Tomov { 0 }, 84ec6859b2STodor Tomov { 0 }, 85ec6859b2STodor Tomov { 0 }, 86ec6859b2STodor Tomov { 100000000, 200000000 }, 87ec6859b2STodor Tomov { 0 }, 88ec6859b2STodor Tomov { 0 }, 89ec6859b2STodor Tomov { 0 } }, 90ec6859b2STodor Tomov .reg = { "csid1" }, 91ec6859b2STodor Tomov .interrupt = { "csid1" } 92ec6859b2STodor Tomov }, 93ec6859b2STodor Tomov }; 94ec6859b2STodor Tomov 959c3e59deSTodor Tomov static const struct resources_ispif ispif_res_8x16 = { 96ec6859b2STodor Tomov /* ISPIF */ 9709a94865STodor Tomov .clock = { "top_ahb", "ahb", "ispif_ahb", 98ec6859b2STodor Tomov "csi0", "csi0_pix", "csi0_rdi", 99ec6859b2STodor Tomov "csi1", "csi1_pix", "csi1_rdi" }, 10009a94865STodor Tomov .clock_for_reset = { "vfe0", "csi_vfe0" }, 101ec6859b2STodor Tomov .reg = { "ispif", "csi_clk_mux" }, 102ec6859b2STodor Tomov .interrupt = "ispif" 103ec6859b2STodor Tomov 104ec6859b2STodor Tomov }; 105ec6859b2STodor Tomov 1069c3e59deSTodor Tomov static const struct resources vfe_res_8x16[] = { 107ec6859b2STodor Tomov /* VFE0 */ 1089c3e59deSTodor Tomov { 109ec6859b2STodor Tomov .regulator = { NULL }, 11009a94865STodor Tomov .clock = { "top_ahb", "vfe0", "csi_vfe0", 11109a94865STodor Tomov "vfe_ahb", "vfe_axi", "ahb" }, 112ec6859b2STodor Tomov .clock_rate = { { 0 }, 113ec6859b2STodor Tomov { 50000000, 80000000, 100000000, 160000000, 114ec6859b2STodor Tomov 177780000, 200000000, 266670000, 320000000, 115ec6859b2STodor Tomov 400000000, 465000000 }, 116ec6859b2STodor Tomov { 0 }, 117ec6859b2STodor Tomov { 0 }, 118ec6859b2STodor Tomov { 0 }, 119ec6859b2STodor Tomov { 0 }, 120ec6859b2STodor Tomov { 0 }, 121ec6859b2STodor Tomov { 0 }, 122ec6859b2STodor Tomov { 0 } }, 123ec6859b2STodor Tomov .reg = { "vfe0" }, 124ec6859b2STodor Tomov .interrupt = { "vfe0" } 1259c3e59deSTodor Tomov } 1269c3e59deSTodor Tomov }; 1279c3e59deSTodor Tomov 1289c3e59deSTodor Tomov static const struct resources csiphy_res_8x96[] = { 1299c3e59deSTodor Tomov /* CSIPHY0 */ 1309c3e59deSTodor Tomov { 1319c3e59deSTodor Tomov .regulator = { NULL }, 1329c3e59deSTodor Tomov .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" }, 1339c3e59deSTodor Tomov .clock_rate = { { 0 }, 1349c3e59deSTodor Tomov { 0 }, 1359c3e59deSTodor Tomov { 0 }, 1369c3e59deSTodor Tomov { 100000000, 200000000, 266666667 } }, 1379c3e59deSTodor Tomov .reg = { "csiphy0", "csiphy0_clk_mux" }, 1389c3e59deSTodor Tomov .interrupt = { "csiphy0" } 1399c3e59deSTodor Tomov }, 1409c3e59deSTodor Tomov 1419c3e59deSTodor Tomov /* CSIPHY1 */ 1429c3e59deSTodor Tomov { 1439c3e59deSTodor Tomov .regulator = { NULL }, 1449c3e59deSTodor Tomov .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" }, 1459c3e59deSTodor Tomov .clock_rate = { { 0 }, 1469c3e59deSTodor Tomov { 0 }, 1479c3e59deSTodor Tomov { 0 }, 1489c3e59deSTodor Tomov { 100000000, 200000000, 266666667 } }, 1499c3e59deSTodor Tomov .reg = { "csiphy1", "csiphy1_clk_mux" }, 1509c3e59deSTodor Tomov .interrupt = { "csiphy1" } 1519c3e59deSTodor Tomov }, 1529c3e59deSTodor Tomov 1539c3e59deSTodor Tomov /* CSIPHY2 */ 1549c3e59deSTodor Tomov { 1559c3e59deSTodor Tomov .regulator = { NULL }, 1569c3e59deSTodor Tomov .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy2_timer" }, 1579c3e59deSTodor Tomov .clock_rate = { { 0 }, 1589c3e59deSTodor Tomov { 0 }, 1599c3e59deSTodor Tomov { 0 }, 1609c3e59deSTodor Tomov { 100000000, 200000000, 266666667 } }, 1619c3e59deSTodor Tomov .reg = { "csiphy2", "csiphy2_clk_mux" }, 1629c3e59deSTodor Tomov .interrupt = { "csiphy2" } 1639c3e59deSTodor Tomov } 1649c3e59deSTodor Tomov }; 1659c3e59deSTodor Tomov 1669c3e59deSTodor Tomov static const struct resources csid_res_8x96[] = { 1679c3e59deSTodor Tomov /* CSID0 */ 1689c3e59deSTodor Tomov { 1699c3e59deSTodor Tomov .regulator = { "vdda" }, 1709c3e59deSTodor Tomov .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", 1719c3e59deSTodor Tomov "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, 1729c3e59deSTodor Tomov .clock_rate = { { 0 }, 1739c3e59deSTodor Tomov { 0 }, 1749c3e59deSTodor Tomov { 0 }, 1759c3e59deSTodor Tomov { 0 }, 1769c3e59deSTodor Tomov { 100000000, 200000000, 266666667 }, 1779c3e59deSTodor Tomov { 0 }, 1789c3e59deSTodor Tomov { 0 }, 1799c3e59deSTodor Tomov { 0 } }, 1809c3e59deSTodor Tomov .reg = { "csid0" }, 1819c3e59deSTodor Tomov .interrupt = { "csid0" } 1829c3e59deSTodor Tomov }, 1839c3e59deSTodor Tomov 1849c3e59deSTodor Tomov /* CSID1 */ 1859c3e59deSTodor Tomov { 1869c3e59deSTodor Tomov .regulator = { "vdda" }, 1879c3e59deSTodor Tomov .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", 1889c3e59deSTodor Tomov "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, 1899c3e59deSTodor Tomov .clock_rate = { { 0 }, 1909c3e59deSTodor Tomov { 0 }, 1919c3e59deSTodor Tomov { 0 }, 1929c3e59deSTodor Tomov { 0 }, 1939c3e59deSTodor Tomov { 100000000, 200000000, 266666667 }, 1949c3e59deSTodor Tomov { 0 }, 1959c3e59deSTodor Tomov { 0 }, 1969c3e59deSTodor Tomov { 0 } }, 1979c3e59deSTodor Tomov .reg = { "csid1" }, 1989c3e59deSTodor Tomov .interrupt = { "csid1" } 1999c3e59deSTodor Tomov }, 2009c3e59deSTodor Tomov 2019c3e59deSTodor Tomov /* CSID2 */ 2029c3e59deSTodor Tomov { 2039c3e59deSTodor Tomov .regulator = { "vdda" }, 2049c3e59deSTodor Tomov .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb", 2059c3e59deSTodor Tomov "csi2", "csi2_phy", "csi2_pix", "csi2_rdi" }, 2069c3e59deSTodor Tomov .clock_rate = { { 0 }, 2079c3e59deSTodor Tomov { 0 }, 2089c3e59deSTodor Tomov { 0 }, 2099c3e59deSTodor Tomov { 0 }, 2109c3e59deSTodor Tomov { 100000000, 200000000, 266666667 }, 2119c3e59deSTodor Tomov { 0 }, 2129c3e59deSTodor Tomov { 0 }, 2139c3e59deSTodor Tomov { 0 } }, 2149c3e59deSTodor Tomov .reg = { "csid2" }, 2159c3e59deSTodor Tomov .interrupt = { "csid2" } 2169c3e59deSTodor Tomov }, 2179c3e59deSTodor Tomov 2189c3e59deSTodor Tomov /* CSID3 */ 2199c3e59deSTodor Tomov { 2209c3e59deSTodor Tomov .regulator = { "vdda" }, 2219c3e59deSTodor Tomov .clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb", 2229c3e59deSTodor Tomov "csi3", "csi3_phy", "csi3_pix", "csi3_rdi" }, 2239c3e59deSTodor Tomov .clock_rate = { { 0 }, 2249c3e59deSTodor Tomov { 0 }, 2259c3e59deSTodor Tomov { 0 }, 2269c3e59deSTodor Tomov { 0 }, 2279c3e59deSTodor Tomov { 100000000, 200000000, 266666667 }, 2289c3e59deSTodor Tomov { 0 }, 2299c3e59deSTodor Tomov { 0 }, 2309c3e59deSTodor Tomov { 0 } }, 2319c3e59deSTodor Tomov .reg = { "csid3" }, 2329c3e59deSTodor Tomov .interrupt = { "csid3" } 2339c3e59deSTodor Tomov } 2349c3e59deSTodor Tomov }; 2359c3e59deSTodor Tomov 2369c3e59deSTodor Tomov static const struct resources_ispif ispif_res_8x96 = { 2379c3e59deSTodor Tomov /* ISPIF */ 2389c3e59deSTodor Tomov .clock = { "top_ahb", "ahb", "ispif_ahb", 2399c3e59deSTodor Tomov "csi0", "csi0_pix", "csi0_rdi", 2409c3e59deSTodor Tomov "csi1", "csi1_pix", "csi1_rdi", 2419c3e59deSTodor Tomov "csi2", "csi2_pix", "csi2_rdi", 2429c3e59deSTodor Tomov "csi3", "csi3_pix", "csi3_rdi" }, 2439c3e59deSTodor Tomov .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" }, 2449c3e59deSTodor Tomov .reg = { "ispif", "csi_clk_mux" }, 2459c3e59deSTodor Tomov .interrupt = "ispif" 2469c3e59deSTodor Tomov }; 2479c3e59deSTodor Tomov 2489c3e59deSTodor Tomov static const struct resources vfe_res_8x96[] = { 2499c3e59deSTodor Tomov /* VFE0 */ 2509c3e59deSTodor Tomov { 2519c3e59deSTodor Tomov .regulator = { NULL }, 2529c3e59deSTodor Tomov .clock = { "top_ahb", "ahb", "vfe0", "csi_vfe0", "vfe_ahb", 2539c3e59deSTodor Tomov "vfe0_ahb", "vfe_axi", "vfe0_stream"}, 2549c3e59deSTodor Tomov .clock_rate = { { 0 }, 2559c3e59deSTodor Tomov { 0 }, 2569c3e59deSTodor Tomov { 75000000, 100000000, 300000000, 2579c3e59deSTodor Tomov 320000000, 480000000, 600000000 }, 2589c3e59deSTodor Tomov { 0 }, 2599c3e59deSTodor Tomov { 0 }, 2609c3e59deSTodor Tomov { 0 }, 2619c3e59deSTodor Tomov { 0 }, 2629c3e59deSTodor Tomov { 0 } }, 2639c3e59deSTodor Tomov .reg = { "vfe0" }, 2649c3e59deSTodor Tomov .interrupt = { "vfe0" } 2659c3e59deSTodor Tomov }, 2669c3e59deSTodor Tomov 2679c3e59deSTodor Tomov /* VFE1 */ 2689c3e59deSTodor Tomov { 2699c3e59deSTodor Tomov .regulator = { NULL }, 2709c3e59deSTodor Tomov .clock = { "top_ahb", "ahb", "vfe1", "csi_vfe1", "vfe_ahb", 2719c3e59deSTodor Tomov "vfe1_ahb", "vfe_axi", "vfe1_stream"}, 2729c3e59deSTodor Tomov .clock_rate = { { 0 }, 2739c3e59deSTodor Tomov { 0 }, 2749c3e59deSTodor Tomov { 75000000, 100000000, 300000000, 2759c3e59deSTodor Tomov 320000000, 480000000, 600000000 }, 2769c3e59deSTodor Tomov { 0 }, 2779c3e59deSTodor Tomov { 0 }, 2789c3e59deSTodor Tomov { 0 }, 2799c3e59deSTodor Tomov { 0 }, 2809c3e59deSTodor Tomov { 0 } }, 2819c3e59deSTodor Tomov .reg = { "vfe1" }, 2829c3e59deSTodor Tomov .interrupt = { "vfe1" } 2839c3e59deSTodor Tomov } 284ec6859b2STodor Tomov }; 285ec6859b2STodor Tomov 286ec6859b2STodor Tomov /* 287ec6859b2STodor Tomov * camss_add_clock_margin - Add margin to clock frequency rate 288ec6859b2STodor Tomov * @rate: Clock frequency rate 289ec6859b2STodor Tomov * 290ec6859b2STodor Tomov * When making calculations with physical clock frequency values 291ec6859b2STodor Tomov * some safety margin must be added. Add it. 292ec6859b2STodor Tomov */ 293ec6859b2STodor Tomov inline void camss_add_clock_margin(u64 *rate) 294ec6859b2STodor Tomov { 295ec6859b2STodor Tomov *rate *= CAMSS_CLOCK_MARGIN_NUMERATOR; 296ec6859b2STodor Tomov *rate = div_u64(*rate, CAMSS_CLOCK_MARGIN_DENOMINATOR); 297ec6859b2STodor Tomov } 298ec6859b2STodor Tomov 299ec6859b2STodor Tomov /* 300ec6859b2STodor Tomov * camss_enable_clocks - Enable multiple clocks 301ec6859b2STodor Tomov * @nclocks: Number of clocks in clock array 302ec6859b2STodor Tomov * @clock: Clock array 303ec6859b2STodor Tomov * @dev: Device 304ec6859b2STodor Tomov * 305ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 306ec6859b2STodor Tomov */ 307ec6859b2STodor Tomov int camss_enable_clocks(int nclocks, struct camss_clock *clock, 308ec6859b2STodor Tomov struct device *dev) 309ec6859b2STodor Tomov { 310ec6859b2STodor Tomov int ret; 311ec6859b2STodor Tomov int i; 312ec6859b2STodor Tomov 313ec6859b2STodor Tomov for (i = 0; i < nclocks; i++) { 314ec6859b2STodor Tomov ret = clk_prepare_enable(clock[i].clk); 315ec6859b2STodor Tomov if (ret) { 316ec6859b2STodor Tomov dev_err(dev, "clock enable failed: %d\n", ret); 317ec6859b2STodor Tomov goto error; 318ec6859b2STodor Tomov } 319ec6859b2STodor Tomov } 320ec6859b2STodor Tomov 321ec6859b2STodor Tomov return 0; 322ec6859b2STodor Tomov 323ec6859b2STodor Tomov error: 324ec6859b2STodor Tomov for (i--; i >= 0; i--) 325ec6859b2STodor Tomov clk_disable_unprepare(clock[i].clk); 326ec6859b2STodor Tomov 327ec6859b2STodor Tomov return ret; 328ec6859b2STodor Tomov } 329ec6859b2STodor Tomov 330ec6859b2STodor Tomov /* 331ec6859b2STodor Tomov * camss_disable_clocks - Disable multiple clocks 332ec6859b2STodor Tomov * @nclocks: Number of clocks in clock array 333ec6859b2STodor Tomov * @clock: Clock array 334ec6859b2STodor Tomov */ 335ec6859b2STodor Tomov void camss_disable_clocks(int nclocks, struct camss_clock *clock) 336ec6859b2STodor Tomov { 337ec6859b2STodor Tomov int i; 338ec6859b2STodor Tomov 339ec6859b2STodor Tomov for (i = nclocks - 1; i >= 0; i--) 340ec6859b2STodor Tomov clk_disable_unprepare(clock[i].clk); 341ec6859b2STodor Tomov } 342ec6859b2STodor Tomov 343ec6859b2STodor Tomov /* 344ec6859b2STodor Tomov * camss_find_sensor - Find a linked media entity which represents a sensor 345ec6859b2STodor Tomov * @entity: Media entity to start searching from 346ec6859b2STodor Tomov * 347ec6859b2STodor Tomov * Return a pointer to sensor media entity or NULL if not found 348ec6859b2STodor Tomov */ 34925f5c34bSTodor Tomov struct media_entity *camss_find_sensor(struct media_entity *entity) 350ec6859b2STodor Tomov { 351ec6859b2STodor Tomov struct media_pad *pad; 352ec6859b2STodor Tomov 353ec6859b2STodor Tomov while (1) { 354ec6859b2STodor Tomov pad = &entity->pads[0]; 355ec6859b2STodor Tomov if (!(pad->flags & MEDIA_PAD_FL_SINK)) 356ec6859b2STodor Tomov return NULL; 357ec6859b2STodor Tomov 358ec6859b2STodor Tomov pad = media_entity_remote_pad(pad); 359ec6859b2STodor Tomov if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 360ec6859b2STodor Tomov return NULL; 361ec6859b2STodor Tomov 362ec6859b2STodor Tomov entity = pad->entity; 363ec6859b2STodor Tomov 364ec6859b2STodor Tomov if (entity->function == MEDIA_ENT_F_CAM_SENSOR) 365ec6859b2STodor Tomov return entity; 366ec6859b2STodor Tomov } 367ec6859b2STodor Tomov } 368ec6859b2STodor Tomov 369ec6859b2STodor Tomov /* 370ec6859b2STodor Tomov * camss_get_pixel_clock - Get pixel clock rate from sensor 371ec6859b2STodor Tomov * @entity: Media entity in the current pipeline 372ec6859b2STodor Tomov * @pixel_clock: Received pixel clock value 373ec6859b2STodor Tomov * 374ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 375ec6859b2STodor Tomov */ 376ec6859b2STodor Tomov int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock) 377ec6859b2STodor Tomov { 378ec6859b2STodor Tomov struct media_entity *sensor; 379ec6859b2STodor Tomov struct v4l2_subdev *subdev; 380ec6859b2STodor Tomov struct v4l2_ctrl *ctrl; 381ec6859b2STodor Tomov 382ec6859b2STodor Tomov sensor = camss_find_sensor(entity); 383ec6859b2STodor Tomov if (!sensor) 384ec6859b2STodor Tomov return -ENODEV; 385ec6859b2STodor Tomov 386ec6859b2STodor Tomov subdev = media_entity_to_v4l2_subdev(sensor); 387ec6859b2STodor Tomov 388ec6859b2STodor Tomov ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE); 389ec6859b2STodor Tomov 390ec6859b2STodor Tomov if (!ctrl) 391ec6859b2STodor Tomov return -EINVAL; 392ec6859b2STodor Tomov 393ec6859b2STodor Tomov *pixel_clock = v4l2_ctrl_g_ctrl_int64(ctrl); 394ec6859b2STodor Tomov 395ec6859b2STodor Tomov return 0; 396ec6859b2STodor Tomov } 397ec6859b2STodor Tomov 39802afa816STodor Tomov int camss_pm_domain_on(struct camss *camss, int id) 39902afa816STodor Tomov { 40002afa816STodor Tomov if (camss->version == CAMSS_8x96) { 40102afa816STodor Tomov camss->genpd_link[id] = device_link_add(camss->dev, 40202afa816STodor Tomov camss->genpd[id], DL_FLAG_STATELESS | 40302afa816STodor Tomov DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); 40402afa816STodor Tomov 40502afa816STodor Tomov if (!camss->genpd_link[id]) 40602afa816STodor Tomov return -EINVAL; 40702afa816STodor Tomov } 40802afa816STodor Tomov 40902afa816STodor Tomov return 0; 41002afa816STodor Tomov } 41102afa816STodor Tomov 41202afa816STodor Tomov void camss_pm_domain_off(struct camss *camss, int id) 41302afa816STodor Tomov { 41402afa816STodor Tomov if (camss->version == CAMSS_8x96) 41502afa816STodor Tomov device_link_del(camss->genpd_link[id]); 41602afa816STodor Tomov } 41702afa816STodor Tomov 418ec6859b2STodor Tomov /* 419ec6859b2STodor Tomov * camss_of_parse_endpoint_node - Parse port endpoint node 420ec6859b2STodor Tomov * @dev: Device 421ec6859b2STodor Tomov * @node: Device node to be parsed 422ec6859b2STodor Tomov * @csd: Parsed data from port endpoint node 423ec6859b2STodor Tomov * 424ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 425ec6859b2STodor Tomov */ 426ec6859b2STodor Tomov static int camss_of_parse_endpoint_node(struct device *dev, 427ec6859b2STodor Tomov struct device_node *node, 428ec6859b2STodor Tomov struct camss_async_subdev *csd) 429ec6859b2STodor Tomov { 430ec6859b2STodor Tomov struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg; 431ec6859b2STodor Tomov struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2; 432ec6859b2STodor Tomov struct v4l2_fwnode_endpoint vep = { { 0 } }; 433ec6859b2STodor Tomov unsigned int i; 434ec6859b2STodor Tomov 435ec6859b2STodor Tomov v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); 436ec6859b2STodor Tomov 437ec6859b2STodor Tomov csd->interface.csiphy_id = vep.base.port; 438ec6859b2STodor Tomov 439ec6859b2STodor Tomov mipi_csi2 = &vep.bus.mipi_csi2; 440ec6859b2STodor Tomov lncfg->clk.pos = mipi_csi2->clock_lane; 441ec6859b2STodor Tomov lncfg->clk.pol = mipi_csi2->lane_polarities[0]; 442ec6859b2STodor Tomov lncfg->num_data = mipi_csi2->num_data_lanes; 443ec6859b2STodor Tomov 444ec6859b2STodor Tomov lncfg->data = devm_kcalloc(dev, 445ec6859b2STodor Tomov lncfg->num_data, sizeof(*lncfg->data), 446ec6859b2STodor Tomov GFP_KERNEL); 447ec6859b2STodor Tomov if (!lncfg->data) 448ec6859b2STodor Tomov return -ENOMEM; 449ec6859b2STodor Tomov 450ec6859b2STodor Tomov for (i = 0; i < lncfg->num_data; i++) { 451ec6859b2STodor Tomov lncfg->data[i].pos = mipi_csi2->data_lanes[i]; 452ec6859b2STodor Tomov lncfg->data[i].pol = mipi_csi2->lane_polarities[i + 1]; 453ec6859b2STodor Tomov } 454ec6859b2STodor Tomov 455ec6859b2STodor Tomov return 0; 456ec6859b2STodor Tomov } 457ec6859b2STodor Tomov 458ec6859b2STodor Tomov /* 459ec6859b2STodor Tomov * camss_of_parse_ports - Parse ports node 460ec6859b2STodor Tomov * @dev: Device 461ec6859b2STodor Tomov * @notifier: v4l2_device notifier data 462ec6859b2STodor Tomov * 463ec6859b2STodor Tomov * Return number of "port" nodes found in "ports" node 464ec6859b2STodor Tomov */ 465d079f94cSSteve Longerbeam static int camss_of_parse_ports(struct camss *camss) 466ec6859b2STodor Tomov { 467d079f94cSSteve Longerbeam struct device *dev = camss->dev; 468ec6859b2STodor Tomov struct device_node *node = NULL; 469ec6859b2STodor Tomov struct device_node *remote = NULL; 470d079f94cSSteve Longerbeam int ret, num_subdevs = 0; 471ec6859b2STodor Tomov 472d079f94cSSteve Longerbeam for_each_endpoint_of_node(dev->of_node, node) { 473ec6859b2STodor Tomov struct camss_async_subdev *csd; 474d079f94cSSteve Longerbeam struct v4l2_async_subdev *asd; 475ec6859b2STodor Tomov 476ec6859b2STodor Tomov if (!of_device_is_available(node)) 477ec6859b2STodor Tomov continue; 478ec6859b2STodor Tomov 479ec6859b2STodor Tomov remote = of_graph_get_remote_port_parent(node); 480ec6859b2STodor Tomov if (!remote) { 481ec6859b2STodor Tomov dev_err(dev, "Cannot get remote parent\n"); 482d079f94cSSteve Longerbeam ret = -EINVAL; 483d079f94cSSteve Longerbeam goto err_cleanup; 484ec6859b2STodor Tomov } 485ec6859b2STodor Tomov 486d079f94cSSteve Longerbeam asd = v4l2_async_notifier_add_fwnode_subdev( 487d079f94cSSteve Longerbeam &camss->notifier, of_fwnode_handle(remote), 488d079f94cSSteve Longerbeam sizeof(*csd)); 489016413d9SSakari Ailus of_node_put(remote); 490d079f94cSSteve Longerbeam if (IS_ERR(asd)) { 491d079f94cSSteve Longerbeam ret = PTR_ERR(asd); 492d079f94cSSteve Longerbeam goto err_cleanup; 493ec6859b2STodor Tomov } 494ec6859b2STodor Tomov 495d079f94cSSteve Longerbeam csd = container_of(asd, struct camss_async_subdev, asd); 496d079f94cSSteve Longerbeam 497d079f94cSSteve Longerbeam ret = camss_of_parse_endpoint_node(dev, node, csd); 498d079f94cSSteve Longerbeam if (ret < 0) 499d079f94cSSteve Longerbeam goto err_cleanup; 500d079f94cSSteve Longerbeam 501d079f94cSSteve Longerbeam num_subdevs++; 502d079f94cSSteve Longerbeam } 503d079f94cSSteve Longerbeam 504d079f94cSSteve Longerbeam return num_subdevs; 505d079f94cSSteve Longerbeam 506d079f94cSSteve Longerbeam err_cleanup: 507d079f94cSSteve Longerbeam v4l2_async_notifier_cleanup(&camss->notifier); 508d079f94cSSteve Longerbeam of_node_put(node); 509d079f94cSSteve Longerbeam return ret; 510ec6859b2STodor Tomov } 511ec6859b2STodor Tomov 512ec6859b2STodor Tomov /* 513ec6859b2STodor Tomov * camss_init_subdevices - Initialize subdev structures and resources 514ec6859b2STodor Tomov * @camss: CAMSS device 515ec6859b2STodor Tomov * 516ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 517ec6859b2STodor Tomov */ 518ec6859b2STodor Tomov static int camss_init_subdevices(struct camss *camss) 519ec6859b2STodor Tomov { 5209c3e59deSTodor Tomov const struct resources *csiphy_res; 5219c3e59deSTodor Tomov const struct resources *csid_res; 5229c3e59deSTodor Tomov const struct resources_ispif *ispif_res; 5239c3e59deSTodor Tomov const struct resources *vfe_res; 524ec6859b2STodor Tomov unsigned int i; 525ec6859b2STodor Tomov int ret; 526ec6859b2STodor Tomov 5279c3e59deSTodor Tomov if (camss->version == CAMSS_8x16) { 5289c3e59deSTodor Tomov csiphy_res = csiphy_res_8x16; 5299c3e59deSTodor Tomov csid_res = csid_res_8x16; 5309c3e59deSTodor Tomov ispif_res = &ispif_res_8x16; 5319c3e59deSTodor Tomov vfe_res = vfe_res_8x16; 5329c3e59deSTodor Tomov } else if (camss->version == CAMSS_8x96) { 5339c3e59deSTodor Tomov csiphy_res = csiphy_res_8x96; 5349c3e59deSTodor Tomov csid_res = csid_res_8x96; 5359c3e59deSTodor Tomov ispif_res = &ispif_res_8x96; 5369c3e59deSTodor Tomov vfe_res = vfe_res_8x96; 5379c3e59deSTodor Tomov } else { 5389c3e59deSTodor Tomov return -EINVAL; 5399c3e59deSTodor Tomov } 5409c3e59deSTodor Tomov 5419c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) { 5429c3e59deSTodor Tomov ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i], 543ec6859b2STodor Tomov &csiphy_res[i], i); 544ec6859b2STodor Tomov if (ret < 0) { 545ec6859b2STodor Tomov dev_err(camss->dev, 546ec6859b2STodor Tomov "Failed to init csiphy%d sub-device: %d\n", 547ec6859b2STodor Tomov i, ret); 548ec6859b2STodor Tomov return ret; 549ec6859b2STodor Tomov } 550ec6859b2STodor Tomov } 551ec6859b2STodor Tomov 5529c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) { 5539c3e59deSTodor Tomov ret = msm_csid_subdev_init(camss, &camss->csid[i], 554ec6859b2STodor Tomov &csid_res[i], i); 555ec6859b2STodor Tomov if (ret < 0) { 556ec6859b2STodor Tomov dev_err(camss->dev, 557ec6859b2STodor Tomov "Failed to init csid%d sub-device: %d\n", 558ec6859b2STodor Tomov i, ret); 559ec6859b2STodor Tomov return ret; 560ec6859b2STodor Tomov } 561ec6859b2STodor Tomov } 562ec6859b2STodor Tomov 5639c3e59deSTodor Tomov ret = msm_ispif_subdev_init(&camss->ispif, ispif_res); 564ec6859b2STodor Tomov if (ret < 0) { 565ec6859b2STodor Tomov dev_err(camss->dev, "Failed to init ispif sub-device: %d\n", 566ec6859b2STodor Tomov ret); 567ec6859b2STodor Tomov return ret; 568ec6859b2STodor Tomov } 569ec6859b2STodor Tomov 5709c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) { 5719c3e59deSTodor Tomov ret = msm_vfe_subdev_init(camss, &camss->vfe[i], 5729c3e59deSTodor Tomov &vfe_res[i], i); 573ec6859b2STodor Tomov if (ret < 0) { 5749c3e59deSTodor Tomov dev_err(camss->dev, 5759c3e59deSTodor Tomov "Fail to init vfe%d sub-device: %d\n", i, ret); 576ec6859b2STodor Tomov return ret; 577ec6859b2STodor Tomov } 5789c3e59deSTodor Tomov } 579ec6859b2STodor Tomov 580ec6859b2STodor Tomov return 0; 581ec6859b2STodor Tomov } 582ec6859b2STodor Tomov 583ec6859b2STodor Tomov /* 584ec6859b2STodor Tomov * camss_register_entities - Register subdev nodes and create links 585ec6859b2STodor Tomov * @camss: CAMSS device 586ec6859b2STodor Tomov * 587ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 588ec6859b2STodor Tomov */ 589ec6859b2STodor Tomov static int camss_register_entities(struct camss *camss) 590ec6859b2STodor Tomov { 5919c3e59deSTodor Tomov int i, j, k; 592ec6859b2STodor Tomov int ret; 593ec6859b2STodor Tomov 5949c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) { 595ec6859b2STodor Tomov ret = msm_csiphy_register_entity(&camss->csiphy[i], 596ec6859b2STodor Tomov &camss->v4l2_dev); 597ec6859b2STodor Tomov if (ret < 0) { 598ec6859b2STodor Tomov dev_err(camss->dev, 599ec6859b2STodor Tomov "Failed to register csiphy%d entity: %d\n", 600ec6859b2STodor Tomov i, ret); 601ec6859b2STodor Tomov goto err_reg_csiphy; 602ec6859b2STodor Tomov } 603ec6859b2STodor Tomov } 604ec6859b2STodor Tomov 6059c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) { 606ec6859b2STodor Tomov ret = msm_csid_register_entity(&camss->csid[i], 607ec6859b2STodor Tomov &camss->v4l2_dev); 608ec6859b2STodor Tomov if (ret < 0) { 609ec6859b2STodor Tomov dev_err(camss->dev, 610ec6859b2STodor Tomov "Failed to register csid%d entity: %d\n", 611ec6859b2STodor Tomov i, ret); 612ec6859b2STodor Tomov goto err_reg_csid; 613ec6859b2STodor Tomov } 614ec6859b2STodor Tomov } 615ec6859b2STodor Tomov 616ec6859b2STodor Tomov ret = msm_ispif_register_entities(&camss->ispif, &camss->v4l2_dev); 617ec6859b2STodor Tomov if (ret < 0) { 618ec6859b2STodor Tomov dev_err(camss->dev, "Failed to register ispif entities: %d\n", 619ec6859b2STodor Tomov ret); 620ec6859b2STodor Tomov goto err_reg_ispif; 621ec6859b2STodor Tomov } 622ec6859b2STodor Tomov 6239c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) { 6249c3e59deSTodor Tomov ret = msm_vfe_register_entities(&camss->vfe[i], 6259c3e59deSTodor Tomov &camss->v4l2_dev); 626ec6859b2STodor Tomov if (ret < 0) { 6279c3e59deSTodor Tomov dev_err(camss->dev, 6289c3e59deSTodor Tomov "Failed to register vfe%d entities: %d\n", 6299c3e59deSTodor Tomov i, ret); 630ec6859b2STodor Tomov goto err_reg_vfe; 631ec6859b2STodor Tomov } 6329c3e59deSTodor Tomov } 633ec6859b2STodor Tomov 6349c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) { 6359c3e59deSTodor Tomov for (j = 0; j < camss->csid_num; j++) { 636ec6859b2STodor Tomov ret = media_create_pad_link( 637ec6859b2STodor Tomov &camss->csiphy[i].subdev.entity, 638ec6859b2STodor Tomov MSM_CSIPHY_PAD_SRC, 639ec6859b2STodor Tomov &camss->csid[j].subdev.entity, 640ec6859b2STodor Tomov MSM_CSID_PAD_SINK, 641ec6859b2STodor Tomov 0); 642ec6859b2STodor Tomov if (ret < 0) { 643ec6859b2STodor Tomov dev_err(camss->dev, 644ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 645ec6859b2STodor Tomov camss->csiphy[i].subdev.entity.name, 646ec6859b2STodor Tomov camss->csid[j].subdev.entity.name, 647ec6859b2STodor Tomov ret); 648ec6859b2STodor Tomov goto err_link; 649ec6859b2STodor Tomov } 650ec6859b2STodor Tomov } 651ec6859b2STodor Tomov } 652ec6859b2STodor Tomov 6539c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) { 6549c3e59deSTodor Tomov for (j = 0; j < camss->ispif.line_num; j++) { 655ec6859b2STodor Tomov ret = media_create_pad_link( 656ec6859b2STodor Tomov &camss->csid[i].subdev.entity, 657ec6859b2STodor Tomov MSM_CSID_PAD_SRC, 658ec6859b2STodor Tomov &camss->ispif.line[j].subdev.entity, 659ec6859b2STodor Tomov MSM_ISPIF_PAD_SINK, 660ec6859b2STodor Tomov 0); 661ec6859b2STodor Tomov if (ret < 0) { 662ec6859b2STodor Tomov dev_err(camss->dev, 663ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 664ec6859b2STodor Tomov camss->csid[i].subdev.entity.name, 665ec6859b2STodor Tomov camss->ispif.line[j].subdev.entity.name, 666ec6859b2STodor Tomov ret); 667ec6859b2STodor Tomov goto err_link; 668ec6859b2STodor Tomov } 669ec6859b2STodor Tomov } 670ec6859b2STodor Tomov } 671ec6859b2STodor Tomov 6729c3e59deSTodor Tomov for (i = 0; i < camss->ispif.line_num; i++) 6739c3e59deSTodor Tomov for (k = 0; k < camss->vfe_num; k++) 6749c3e59deSTodor Tomov for (j = 0; j < ARRAY_SIZE(camss->vfe[k].line); j++) { 675ec6859b2STodor Tomov ret = media_create_pad_link( 676ec6859b2STodor Tomov &camss->ispif.line[i].subdev.entity, 677ec6859b2STodor Tomov MSM_ISPIF_PAD_SRC, 6789c3e59deSTodor Tomov &camss->vfe[k].line[j].subdev.entity, 679ec6859b2STodor Tomov MSM_VFE_PAD_SINK, 680ec6859b2STodor Tomov 0); 681ec6859b2STodor Tomov if (ret < 0) { 682ec6859b2STodor Tomov dev_err(camss->dev, 683ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 684ec6859b2STodor Tomov camss->ispif.line[i].subdev.entity.name, 6859c3e59deSTodor Tomov camss->vfe[k].line[j].subdev.entity.name, 686ec6859b2STodor Tomov ret); 687ec6859b2STodor Tomov goto err_link; 688ec6859b2STodor Tomov } 689ec6859b2STodor Tomov } 690ec6859b2STodor Tomov 691ec6859b2STodor Tomov return 0; 692ec6859b2STodor Tomov 693ec6859b2STodor Tomov err_link: 6949c3e59deSTodor Tomov i = camss->vfe_num; 695ec6859b2STodor Tomov err_reg_vfe: 6969c3e59deSTodor Tomov for (i--; i >= 0; i--) 6979c3e59deSTodor Tomov msm_vfe_unregister_entities(&camss->vfe[i]); 6989c3e59deSTodor Tomov 699ec6859b2STodor Tomov msm_ispif_unregister_entities(&camss->ispif); 700ec6859b2STodor Tomov err_reg_ispif: 701ec6859b2STodor Tomov 7029c3e59deSTodor Tomov i = camss->csid_num; 703ec6859b2STodor Tomov err_reg_csid: 704ec6859b2STodor Tomov for (i--; i >= 0; i--) 705ec6859b2STodor Tomov msm_csid_unregister_entity(&camss->csid[i]); 706ec6859b2STodor Tomov 7079c3e59deSTodor Tomov i = camss->csiphy_num; 708ec6859b2STodor Tomov err_reg_csiphy: 709ec6859b2STodor Tomov for (i--; i >= 0; i--) 710ec6859b2STodor Tomov msm_csiphy_unregister_entity(&camss->csiphy[i]); 711ec6859b2STodor Tomov 712ec6859b2STodor Tomov return ret; 713ec6859b2STodor Tomov } 714ec6859b2STodor Tomov 715ec6859b2STodor Tomov /* 716ec6859b2STodor Tomov * camss_unregister_entities - Unregister subdev nodes 717ec6859b2STodor Tomov * @camss: CAMSS device 718ec6859b2STodor Tomov * 719ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 720ec6859b2STodor Tomov */ 721ec6859b2STodor Tomov static void camss_unregister_entities(struct camss *camss) 722ec6859b2STodor Tomov { 723ec6859b2STodor Tomov unsigned int i; 724ec6859b2STodor Tomov 7259c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) 726ec6859b2STodor Tomov msm_csiphy_unregister_entity(&camss->csiphy[i]); 727ec6859b2STodor Tomov 7289c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) 729ec6859b2STodor Tomov msm_csid_unregister_entity(&camss->csid[i]); 730ec6859b2STodor Tomov 731ec6859b2STodor Tomov msm_ispif_unregister_entities(&camss->ispif); 7329c3e59deSTodor Tomov 7339c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) 7349c3e59deSTodor Tomov msm_vfe_unregister_entities(&camss->vfe[i]); 735ec6859b2STodor Tomov } 736ec6859b2STodor Tomov 737ec6859b2STodor Tomov static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async, 738ec6859b2STodor Tomov struct v4l2_subdev *subdev, 739ec6859b2STodor Tomov struct v4l2_async_subdev *asd) 740ec6859b2STodor Tomov { 741ec6859b2STodor Tomov struct camss *camss = container_of(async, struct camss, notifier); 742ec6859b2STodor Tomov struct camss_async_subdev *csd = 743ec6859b2STodor Tomov container_of(asd, struct camss_async_subdev, asd); 744ec6859b2STodor Tomov u8 id = csd->interface.csiphy_id; 745ec6859b2STodor Tomov struct csiphy_device *csiphy = &camss->csiphy[id]; 746ec6859b2STodor Tomov 747ec6859b2STodor Tomov csiphy->cfg.csi2 = &csd->interface.csi2; 748ec6859b2STodor Tomov subdev->host_priv = csiphy; 749ec6859b2STodor Tomov 750ec6859b2STodor Tomov return 0; 751ec6859b2STodor Tomov } 752ec6859b2STodor Tomov 753ec6859b2STodor Tomov static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async) 754ec6859b2STodor Tomov { 755ec6859b2STodor Tomov struct camss *camss = container_of(async, struct camss, notifier); 756ec6859b2STodor Tomov struct v4l2_device *v4l2_dev = &camss->v4l2_dev; 757ec6859b2STodor Tomov struct v4l2_subdev *sd; 758ec6859b2STodor Tomov int ret; 759ec6859b2STodor Tomov 760ec6859b2STodor Tomov list_for_each_entry(sd, &v4l2_dev->subdevs, list) { 761ec6859b2STodor Tomov if (sd->host_priv) { 762ec6859b2STodor Tomov struct media_entity *sensor = &sd->entity; 763ec6859b2STodor Tomov struct csiphy_device *csiphy = 764ec6859b2STodor Tomov (struct csiphy_device *) sd->host_priv; 765ec6859b2STodor Tomov struct media_entity *input = &csiphy->subdev.entity; 766ec6859b2STodor Tomov unsigned int i; 767ec6859b2STodor Tomov 768ec6859b2STodor Tomov for (i = 0; i < sensor->num_pads; i++) { 769ec6859b2STodor Tomov if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE) 770ec6859b2STodor Tomov break; 771ec6859b2STodor Tomov } 772ec6859b2STodor Tomov if (i == sensor->num_pads) { 773ec6859b2STodor Tomov dev_err(camss->dev, 774ec6859b2STodor Tomov "No source pad in external entity\n"); 775ec6859b2STodor Tomov return -EINVAL; 776ec6859b2STodor Tomov } 777ec6859b2STodor Tomov 778ec6859b2STodor Tomov ret = media_create_pad_link(sensor, i, 779ec6859b2STodor Tomov input, MSM_CSIPHY_PAD_SINK, 780ec6859b2STodor Tomov MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); 781ec6859b2STodor Tomov if (ret < 0) { 782ec6859b2STodor Tomov dev_err(camss->dev, 783ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 784ec6859b2STodor Tomov sensor->name, input->name, ret); 785ec6859b2STodor Tomov return ret; 786ec6859b2STodor Tomov } 787ec6859b2STodor Tomov } 788ec6859b2STodor Tomov } 789ec6859b2STodor Tomov 790ec6859b2STodor Tomov ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); 791ec6859b2STodor Tomov if (ret < 0) 792ec6859b2STodor Tomov return ret; 793ec6859b2STodor Tomov 794ec6859b2STodor Tomov return media_device_register(&camss->media_dev); 795ec6859b2STodor Tomov } 796ec6859b2STodor Tomov 797ec6859b2STodor Tomov static const struct v4l2_async_notifier_operations camss_subdev_notifier_ops = { 798ec6859b2STodor Tomov .bound = camss_subdev_notifier_bound, 799ec6859b2STodor Tomov .complete = camss_subdev_notifier_complete, 800ec6859b2STodor Tomov }; 801ec6859b2STodor Tomov 802ec6859b2STodor Tomov static const struct media_device_ops camss_media_ops = { 803ec6859b2STodor Tomov .link_notify = v4l2_pipeline_link_notify, 804ec6859b2STodor Tomov }; 805ec6859b2STodor Tomov 806ec6859b2STodor Tomov /* 807ec6859b2STodor Tomov * camss_probe - Probe CAMSS platform device 808ec6859b2STodor Tomov * @pdev: Pointer to CAMSS platform device 809ec6859b2STodor Tomov * 810ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 811ec6859b2STodor Tomov */ 812ec6859b2STodor Tomov static int camss_probe(struct platform_device *pdev) 813ec6859b2STodor Tomov { 814ec6859b2STodor Tomov struct device *dev = &pdev->dev; 815ec6859b2STodor Tomov struct camss *camss; 816d079f94cSSteve Longerbeam int num_subdevs, ret; 817ec6859b2STodor Tomov 818ec6859b2STodor Tomov camss = kzalloc(sizeof(*camss), GFP_KERNEL); 819ec6859b2STodor Tomov if (!camss) 820ec6859b2STodor Tomov return -ENOMEM; 821ec6859b2STodor Tomov 822ec6859b2STodor Tomov atomic_set(&camss->ref_count, 0); 823ec6859b2STodor Tomov camss->dev = dev; 824ec6859b2STodor Tomov platform_set_drvdata(pdev, camss); 825ec6859b2STodor Tomov 8269c3e59deSTodor Tomov if (of_device_is_compatible(dev->of_node, "qcom,msm8916-camss")) { 8279c3e59deSTodor Tomov camss->version = CAMSS_8x16; 8289c3e59deSTodor Tomov camss->csiphy_num = 2; 8299c3e59deSTodor Tomov camss->csid_num = 2; 8309c3e59deSTodor Tomov camss->vfe_num = 1; 8319c3e59deSTodor Tomov } else if (of_device_is_compatible(dev->of_node, 8329c3e59deSTodor Tomov "qcom,msm8996-camss")) { 8339c3e59deSTodor Tomov camss->version = CAMSS_8x96; 8349c3e59deSTodor Tomov camss->csiphy_num = 3; 8359c3e59deSTodor Tomov camss->csid_num = 4; 8369c3e59deSTodor Tomov camss->vfe_num = 2; 8379c3e59deSTodor Tomov } else { 8389c3e59deSTodor Tomov return -EINVAL; 8399c3e59deSTodor Tomov } 8409c3e59deSTodor Tomov 84155b51899STodor Tomov camss->csiphy = devm_kcalloc(dev, camss->csiphy_num, 84255b51899STodor Tomov sizeof(*camss->csiphy), GFP_KERNEL); 8439c3e59deSTodor Tomov if (!camss->csiphy) 8449c3e59deSTodor Tomov return -ENOMEM; 8459c3e59deSTodor Tomov 84655b51899STodor Tomov camss->csid = devm_kcalloc(dev, camss->csid_num, sizeof(*camss->csid), 8479c3e59deSTodor Tomov GFP_KERNEL); 8489c3e59deSTodor Tomov if (!camss->csid) 8499c3e59deSTodor Tomov return -ENOMEM; 8509c3e59deSTodor Tomov 85155b51899STodor Tomov camss->vfe = devm_kcalloc(dev, camss->vfe_num, sizeof(*camss->vfe), 85255b51899STodor Tomov GFP_KERNEL); 8539c3e59deSTodor Tomov if (!camss->vfe) 8549c3e59deSTodor Tomov return -ENOMEM; 8559c3e59deSTodor Tomov 856d079f94cSSteve Longerbeam v4l2_async_notifier_init(&camss->notifier); 857d079f94cSSteve Longerbeam 858d079f94cSSteve Longerbeam num_subdevs = camss_of_parse_ports(camss); 859d079f94cSSteve Longerbeam if (num_subdevs < 0) 860d079f94cSSteve Longerbeam return num_subdevs; 861ec6859b2STodor Tomov 862ec6859b2STodor Tomov ret = camss_init_subdevices(camss); 863ec6859b2STodor Tomov if (ret < 0) 864d079f94cSSteve Longerbeam goto err_cleanup; 865ec6859b2STodor Tomov 866ec6859b2STodor Tomov ret = dma_set_mask_and_coherent(dev, 0xffffffff); 867ec6859b2STodor Tomov if (ret) 868d079f94cSSteve Longerbeam goto err_cleanup; 869ec6859b2STodor Tomov 870ec6859b2STodor Tomov camss->media_dev.dev = camss->dev; 871c0decac1SMauro Carvalho Chehab strscpy(camss->media_dev.model, "Qualcomm Camera Subsystem", 872ec6859b2STodor Tomov sizeof(camss->media_dev.model)); 873ec6859b2STodor Tomov camss->media_dev.ops = &camss_media_ops; 874ec6859b2STodor Tomov media_device_init(&camss->media_dev); 875ec6859b2STodor Tomov 876ec6859b2STodor Tomov camss->v4l2_dev.mdev = &camss->media_dev; 877ec6859b2STodor Tomov ret = v4l2_device_register(camss->dev, &camss->v4l2_dev); 878ec6859b2STodor Tomov if (ret < 0) { 879ec6859b2STodor Tomov dev_err(dev, "Failed to register V4L2 device: %d\n", ret); 880d079f94cSSteve Longerbeam goto err_cleanup; 881ec6859b2STodor Tomov } 882ec6859b2STodor Tomov 883ec6859b2STodor Tomov ret = camss_register_entities(camss); 884ec6859b2STodor Tomov if (ret < 0) 885ec6859b2STodor Tomov goto err_register_entities; 886ec6859b2STodor Tomov 887d079f94cSSteve Longerbeam if (num_subdevs) { 888ec6859b2STodor Tomov camss->notifier.ops = &camss_subdev_notifier_ops; 889ec6859b2STodor Tomov 890ec6859b2STodor Tomov ret = v4l2_async_notifier_register(&camss->v4l2_dev, 891ec6859b2STodor Tomov &camss->notifier); 892ec6859b2STodor Tomov if (ret) { 893ec6859b2STodor Tomov dev_err(dev, 894ec6859b2STodor Tomov "Failed to register async subdev nodes: %d\n", 895ec6859b2STodor Tomov ret); 896ec6859b2STodor Tomov goto err_register_subdevs; 897ec6859b2STodor Tomov } 898ec6859b2STodor Tomov } else { 899ec6859b2STodor Tomov ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); 900ec6859b2STodor Tomov if (ret < 0) { 901ec6859b2STodor Tomov dev_err(dev, "Failed to register subdev nodes: %d\n", 902ec6859b2STodor Tomov ret); 903ec6859b2STodor Tomov goto err_register_subdevs; 904ec6859b2STodor Tomov } 905ec6859b2STodor Tomov 906ec6859b2STodor Tomov ret = media_device_register(&camss->media_dev); 907ec6859b2STodor Tomov if (ret < 0) { 908ec6859b2STodor Tomov dev_err(dev, "Failed to register media device: %d\n", 909ec6859b2STodor Tomov ret); 910ec6859b2STodor Tomov goto err_register_subdevs; 911ec6859b2STodor Tomov } 912ec6859b2STodor Tomov } 913ec6859b2STodor Tomov 91402afa816STodor Tomov if (camss->version == CAMSS_8x96) { 91502afa816STodor Tomov camss->genpd[PM_DOMAIN_VFE0] = dev_pm_domain_attach_by_id( 91602afa816STodor Tomov camss->dev, PM_DOMAIN_VFE0); 91702afa816STodor Tomov if (IS_ERR(camss->genpd[PM_DOMAIN_VFE0])) 91802afa816STodor Tomov return PTR_ERR(camss->genpd[PM_DOMAIN_VFE0]); 91902afa816STodor Tomov 92002afa816STodor Tomov camss->genpd[PM_DOMAIN_VFE1] = dev_pm_domain_attach_by_id( 92102afa816STodor Tomov camss->dev, PM_DOMAIN_VFE1); 92202afa816STodor Tomov if (IS_ERR(camss->genpd[PM_DOMAIN_VFE1])) { 92302afa816STodor Tomov dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], 92402afa816STodor Tomov true); 92502afa816STodor Tomov return PTR_ERR(camss->genpd[PM_DOMAIN_VFE1]); 92602afa816STodor Tomov } 92702afa816STodor Tomov } 92802afa816STodor Tomov 92902afa816STodor Tomov pm_runtime_enable(dev); 93002afa816STodor Tomov 931ec6859b2STodor Tomov return 0; 932ec6859b2STodor Tomov 933ec6859b2STodor Tomov err_register_subdevs: 934ec6859b2STodor Tomov camss_unregister_entities(camss); 935ec6859b2STodor Tomov err_register_entities: 936ec6859b2STodor Tomov v4l2_device_unregister(&camss->v4l2_dev); 937d079f94cSSteve Longerbeam err_cleanup: 938d079f94cSSteve Longerbeam v4l2_async_notifier_cleanup(&camss->notifier); 939ec6859b2STodor Tomov 940ec6859b2STodor Tomov return ret; 941ec6859b2STodor Tomov } 942ec6859b2STodor Tomov 943ec6859b2STodor Tomov void camss_delete(struct camss *camss) 944ec6859b2STodor Tomov { 945ec6859b2STodor Tomov v4l2_device_unregister(&camss->v4l2_dev); 946ec6859b2STodor Tomov media_device_unregister(&camss->media_dev); 947ec6859b2STodor Tomov media_device_cleanup(&camss->media_dev); 948ec6859b2STodor Tomov 94902afa816STodor Tomov pm_runtime_disable(camss->dev); 95002afa816STodor Tomov 95102afa816STodor Tomov if (camss->version == CAMSS_8x96) { 95202afa816STodor Tomov dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true); 95302afa816STodor Tomov dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true); 95402afa816STodor Tomov } 95502afa816STodor Tomov 956ec6859b2STodor Tomov kfree(camss); 957ec6859b2STodor Tomov } 958ec6859b2STodor Tomov 959ec6859b2STodor Tomov /* 960ec6859b2STodor Tomov * camss_remove - Remove CAMSS platform device 961ec6859b2STodor Tomov * @pdev: Pointer to CAMSS platform device 962ec6859b2STodor Tomov * 963ec6859b2STodor Tomov * Always returns 0. 964ec6859b2STodor Tomov */ 965ec6859b2STodor Tomov static int camss_remove(struct platform_device *pdev) 966ec6859b2STodor Tomov { 9679c3e59deSTodor Tomov unsigned int i; 9689c3e59deSTodor Tomov 969ec6859b2STodor Tomov struct camss *camss = platform_get_drvdata(pdev); 970ec6859b2STodor Tomov 9719c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) 9729c3e59deSTodor Tomov msm_vfe_stop_streaming(&camss->vfe[i]); 973ec6859b2STodor Tomov 974ec6859b2STodor Tomov v4l2_async_notifier_unregister(&camss->notifier); 975d079f94cSSteve Longerbeam v4l2_async_notifier_cleanup(&camss->notifier); 976ec6859b2STodor Tomov camss_unregister_entities(camss); 977ec6859b2STodor Tomov 978ec6859b2STodor Tomov if (atomic_read(&camss->ref_count) == 0) 979ec6859b2STodor Tomov camss_delete(camss); 980ec6859b2STodor Tomov 981ec6859b2STodor Tomov return 0; 982ec6859b2STodor Tomov } 983ec6859b2STodor Tomov 984ec6859b2STodor Tomov static const struct of_device_id camss_dt_match[] = { 985ec6859b2STodor Tomov { .compatible = "qcom,msm8916-camss" }, 9869c3e59deSTodor Tomov { .compatible = "qcom,msm8996-camss" }, 987ec6859b2STodor Tomov { } 988ec6859b2STodor Tomov }; 989ec6859b2STodor Tomov 990ec6859b2STodor Tomov MODULE_DEVICE_TABLE(of, camss_dt_match); 991ec6859b2STodor Tomov 99244a9ffd4SArnd Bergmann static int __maybe_unused camss_runtime_suspend(struct device *dev) 99302afa816STodor Tomov { 99402afa816STodor Tomov return 0; 99502afa816STodor Tomov } 99602afa816STodor Tomov 99744a9ffd4SArnd Bergmann static int __maybe_unused camss_runtime_resume(struct device *dev) 99802afa816STodor Tomov { 99902afa816STodor Tomov return 0; 100002afa816STodor Tomov } 100102afa816STodor Tomov 100202afa816STodor Tomov static const struct dev_pm_ops camss_pm_ops = { 100302afa816STodor Tomov SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 100402afa816STodor Tomov pm_runtime_force_resume) 100502afa816STodor Tomov SET_RUNTIME_PM_OPS(camss_runtime_suspend, camss_runtime_resume, NULL) 100602afa816STodor Tomov }; 100702afa816STodor Tomov 1008ec6859b2STodor Tomov static struct platform_driver qcom_camss_driver = { 1009ec6859b2STodor Tomov .probe = camss_probe, 1010ec6859b2STodor Tomov .remove = camss_remove, 1011ec6859b2STodor Tomov .driver = { 1012ec6859b2STodor Tomov .name = "qcom-camss", 1013ec6859b2STodor Tomov .of_match_table = camss_dt_match, 101402afa816STodor Tomov .pm = &camss_pm_ops, 1015ec6859b2STodor Tomov }, 1016ec6859b2STodor Tomov }; 1017ec6859b2STodor Tomov 1018ec6859b2STodor Tomov module_platform_driver(qcom_camss_driver); 1019ec6859b2STodor Tomov 1020ec6859b2STodor Tomov MODULE_ALIAS("platform:qcom-camss"); 1021ec6859b2STodor Tomov MODULE_DESCRIPTION("Qualcomm Camera Subsystem driver"); 1022ec6859b2STodor Tomov MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>"); 1023ec6859b2STodor Tomov MODULE_LICENSE("GPL v2"); 1024