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 */ 349ec6859b2STodor Tomov static 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 */ 465ec6859b2STodor Tomov static int camss_of_parse_ports(struct device *dev, 466ec6859b2STodor Tomov struct v4l2_async_notifier *notifier) 467ec6859b2STodor Tomov { 468ec6859b2STodor Tomov struct device_node *node = NULL; 469ec6859b2STodor Tomov struct device_node *remote = NULL; 470ec6859b2STodor Tomov unsigned int size, i; 471ec6859b2STodor Tomov int ret; 472ec6859b2STodor Tomov 473ec6859b2STodor Tomov while ((node = of_graph_get_next_endpoint(dev->of_node, node))) 474ec6859b2STodor Tomov if (of_device_is_available(node)) 475ec6859b2STodor Tomov notifier->num_subdevs++; 476ec6859b2STodor Tomov 4772004fc09STodor Tomov of_node_put(node); 478ec6859b2STodor Tomov size = sizeof(*notifier->subdevs) * notifier->num_subdevs; 479ec6859b2STodor Tomov notifier->subdevs = devm_kzalloc(dev, size, GFP_KERNEL); 480ec6859b2STodor Tomov if (!notifier->subdevs) { 481ec6859b2STodor Tomov dev_err(dev, "Failed to allocate memory\n"); 482ec6859b2STodor Tomov return -ENOMEM; 483ec6859b2STodor Tomov } 484ec6859b2STodor Tomov 485ec6859b2STodor Tomov i = 0; 486ec6859b2STodor Tomov while ((node = of_graph_get_next_endpoint(dev->of_node, node))) { 487ec6859b2STodor Tomov struct camss_async_subdev *csd; 488ec6859b2STodor Tomov 489ec6859b2STodor Tomov if (!of_device_is_available(node)) 490ec6859b2STodor Tomov continue; 491ec6859b2STodor Tomov 492ec6859b2STodor Tomov csd = devm_kzalloc(dev, sizeof(*csd), GFP_KERNEL); 493ec6859b2STodor Tomov if (!csd) { 494ec6859b2STodor Tomov of_node_put(node); 495ec6859b2STodor Tomov dev_err(dev, "Failed to allocate memory\n"); 496ec6859b2STodor Tomov return -ENOMEM; 497ec6859b2STodor Tomov } 498ec6859b2STodor Tomov 499ec6859b2STodor Tomov notifier->subdevs[i++] = &csd->asd; 500ec6859b2STodor Tomov 501ec6859b2STodor Tomov ret = camss_of_parse_endpoint_node(dev, node, csd); 502ec6859b2STodor Tomov if (ret < 0) { 503ec6859b2STodor Tomov of_node_put(node); 504ec6859b2STodor Tomov return ret; 505ec6859b2STodor Tomov } 506ec6859b2STodor Tomov 507ec6859b2STodor Tomov remote = of_graph_get_remote_port_parent(node); 508ec6859b2STodor Tomov if (!remote) { 509ec6859b2STodor Tomov dev_err(dev, "Cannot get remote parent\n"); 5102004fc09STodor Tomov of_node_put(node); 511ec6859b2STodor Tomov return -EINVAL; 512ec6859b2STodor Tomov } 513ec6859b2STodor Tomov 514ec6859b2STodor Tomov csd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; 515ec6859b2STodor Tomov csd->asd.match.fwnode = of_fwnode_handle(remote); 516ec6859b2STodor Tomov } 5172004fc09STodor Tomov of_node_put(node); 518ec6859b2STodor Tomov 519ec6859b2STodor Tomov return notifier->num_subdevs; 520ec6859b2STodor Tomov } 521ec6859b2STodor Tomov 522ec6859b2STodor Tomov /* 523ec6859b2STodor Tomov * camss_init_subdevices - Initialize subdev structures and resources 524ec6859b2STodor Tomov * @camss: CAMSS device 525ec6859b2STodor Tomov * 526ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 527ec6859b2STodor Tomov */ 528ec6859b2STodor Tomov static int camss_init_subdevices(struct camss *camss) 529ec6859b2STodor Tomov { 5309c3e59deSTodor Tomov const struct resources *csiphy_res; 5319c3e59deSTodor Tomov const struct resources *csid_res; 5329c3e59deSTodor Tomov const struct resources_ispif *ispif_res; 5339c3e59deSTodor Tomov const struct resources *vfe_res; 534ec6859b2STodor Tomov unsigned int i; 535ec6859b2STodor Tomov int ret; 536ec6859b2STodor Tomov 5379c3e59deSTodor Tomov if (camss->version == CAMSS_8x16) { 5389c3e59deSTodor Tomov csiphy_res = csiphy_res_8x16; 5399c3e59deSTodor Tomov csid_res = csid_res_8x16; 5409c3e59deSTodor Tomov ispif_res = &ispif_res_8x16; 5419c3e59deSTodor Tomov vfe_res = vfe_res_8x16; 5429c3e59deSTodor Tomov } else if (camss->version == CAMSS_8x96) { 5439c3e59deSTodor Tomov csiphy_res = csiphy_res_8x96; 5449c3e59deSTodor Tomov csid_res = csid_res_8x96; 5459c3e59deSTodor Tomov ispif_res = &ispif_res_8x96; 5469c3e59deSTodor Tomov vfe_res = vfe_res_8x96; 5479c3e59deSTodor Tomov } else { 5489c3e59deSTodor Tomov return -EINVAL; 5499c3e59deSTodor Tomov } 5509c3e59deSTodor Tomov 5519c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) { 5529c3e59deSTodor Tomov ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i], 553ec6859b2STodor Tomov &csiphy_res[i], i); 554ec6859b2STodor Tomov if (ret < 0) { 555ec6859b2STodor Tomov dev_err(camss->dev, 556ec6859b2STodor Tomov "Failed to init csiphy%d sub-device: %d\n", 557ec6859b2STodor Tomov i, ret); 558ec6859b2STodor Tomov return ret; 559ec6859b2STodor Tomov } 560ec6859b2STodor Tomov } 561ec6859b2STodor Tomov 5629c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) { 5639c3e59deSTodor Tomov ret = msm_csid_subdev_init(camss, &camss->csid[i], 564ec6859b2STodor Tomov &csid_res[i], i); 565ec6859b2STodor Tomov if (ret < 0) { 566ec6859b2STodor Tomov dev_err(camss->dev, 567ec6859b2STodor Tomov "Failed to init csid%d sub-device: %d\n", 568ec6859b2STodor Tomov i, ret); 569ec6859b2STodor Tomov return ret; 570ec6859b2STodor Tomov } 571ec6859b2STodor Tomov } 572ec6859b2STodor Tomov 5739c3e59deSTodor Tomov ret = msm_ispif_subdev_init(&camss->ispif, ispif_res); 574ec6859b2STodor Tomov if (ret < 0) { 575ec6859b2STodor Tomov dev_err(camss->dev, "Failed to init ispif sub-device: %d\n", 576ec6859b2STodor Tomov ret); 577ec6859b2STodor Tomov return ret; 578ec6859b2STodor Tomov } 579ec6859b2STodor Tomov 5809c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) { 5819c3e59deSTodor Tomov ret = msm_vfe_subdev_init(camss, &camss->vfe[i], 5829c3e59deSTodor Tomov &vfe_res[i], i); 583ec6859b2STodor Tomov if (ret < 0) { 5849c3e59deSTodor Tomov dev_err(camss->dev, 5859c3e59deSTodor Tomov "Fail to init vfe%d sub-device: %d\n", i, ret); 586ec6859b2STodor Tomov return ret; 587ec6859b2STodor Tomov } 5889c3e59deSTodor Tomov } 589ec6859b2STodor Tomov 590ec6859b2STodor Tomov return 0; 591ec6859b2STodor Tomov } 592ec6859b2STodor Tomov 593ec6859b2STodor Tomov /* 594ec6859b2STodor Tomov * camss_register_entities - Register subdev nodes and create links 595ec6859b2STodor Tomov * @camss: CAMSS device 596ec6859b2STodor Tomov * 597ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 598ec6859b2STodor Tomov */ 599ec6859b2STodor Tomov static int camss_register_entities(struct camss *camss) 600ec6859b2STodor Tomov { 6019c3e59deSTodor Tomov int i, j, k; 602ec6859b2STodor Tomov int ret; 603ec6859b2STodor Tomov 6049c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) { 605ec6859b2STodor Tomov ret = msm_csiphy_register_entity(&camss->csiphy[i], 606ec6859b2STodor Tomov &camss->v4l2_dev); 607ec6859b2STodor Tomov if (ret < 0) { 608ec6859b2STodor Tomov dev_err(camss->dev, 609ec6859b2STodor Tomov "Failed to register csiphy%d entity: %d\n", 610ec6859b2STodor Tomov i, ret); 611ec6859b2STodor Tomov goto err_reg_csiphy; 612ec6859b2STodor Tomov } 613ec6859b2STodor Tomov } 614ec6859b2STodor Tomov 6159c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) { 616ec6859b2STodor Tomov ret = msm_csid_register_entity(&camss->csid[i], 617ec6859b2STodor Tomov &camss->v4l2_dev); 618ec6859b2STodor Tomov if (ret < 0) { 619ec6859b2STodor Tomov dev_err(camss->dev, 620ec6859b2STodor Tomov "Failed to register csid%d entity: %d\n", 621ec6859b2STodor Tomov i, ret); 622ec6859b2STodor Tomov goto err_reg_csid; 623ec6859b2STodor Tomov } 624ec6859b2STodor Tomov } 625ec6859b2STodor Tomov 626ec6859b2STodor Tomov ret = msm_ispif_register_entities(&camss->ispif, &camss->v4l2_dev); 627ec6859b2STodor Tomov if (ret < 0) { 628ec6859b2STodor Tomov dev_err(camss->dev, "Failed to register ispif entities: %d\n", 629ec6859b2STodor Tomov ret); 630ec6859b2STodor Tomov goto err_reg_ispif; 631ec6859b2STodor Tomov } 632ec6859b2STodor Tomov 6339c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) { 6349c3e59deSTodor Tomov ret = msm_vfe_register_entities(&camss->vfe[i], 6359c3e59deSTodor Tomov &camss->v4l2_dev); 636ec6859b2STodor Tomov if (ret < 0) { 6379c3e59deSTodor Tomov dev_err(camss->dev, 6389c3e59deSTodor Tomov "Failed to register vfe%d entities: %d\n", 6399c3e59deSTodor Tomov i, ret); 640ec6859b2STodor Tomov goto err_reg_vfe; 641ec6859b2STodor Tomov } 6429c3e59deSTodor Tomov } 643ec6859b2STodor Tomov 6449c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) { 6459c3e59deSTodor Tomov for (j = 0; j < camss->csid_num; j++) { 646ec6859b2STodor Tomov ret = media_create_pad_link( 647ec6859b2STodor Tomov &camss->csiphy[i].subdev.entity, 648ec6859b2STodor Tomov MSM_CSIPHY_PAD_SRC, 649ec6859b2STodor Tomov &camss->csid[j].subdev.entity, 650ec6859b2STodor Tomov MSM_CSID_PAD_SINK, 651ec6859b2STodor Tomov 0); 652ec6859b2STodor Tomov if (ret < 0) { 653ec6859b2STodor Tomov dev_err(camss->dev, 654ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 655ec6859b2STodor Tomov camss->csiphy[i].subdev.entity.name, 656ec6859b2STodor Tomov camss->csid[j].subdev.entity.name, 657ec6859b2STodor Tomov ret); 658ec6859b2STodor Tomov goto err_link; 659ec6859b2STodor Tomov } 660ec6859b2STodor Tomov } 661ec6859b2STodor Tomov } 662ec6859b2STodor Tomov 6639c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) { 6649c3e59deSTodor Tomov for (j = 0; j < camss->ispif.line_num; j++) { 665ec6859b2STodor Tomov ret = media_create_pad_link( 666ec6859b2STodor Tomov &camss->csid[i].subdev.entity, 667ec6859b2STodor Tomov MSM_CSID_PAD_SRC, 668ec6859b2STodor Tomov &camss->ispif.line[j].subdev.entity, 669ec6859b2STodor Tomov MSM_ISPIF_PAD_SINK, 670ec6859b2STodor Tomov 0); 671ec6859b2STodor Tomov if (ret < 0) { 672ec6859b2STodor Tomov dev_err(camss->dev, 673ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 674ec6859b2STodor Tomov camss->csid[i].subdev.entity.name, 675ec6859b2STodor Tomov camss->ispif.line[j].subdev.entity.name, 676ec6859b2STodor Tomov ret); 677ec6859b2STodor Tomov goto err_link; 678ec6859b2STodor Tomov } 679ec6859b2STodor Tomov } 680ec6859b2STodor Tomov } 681ec6859b2STodor Tomov 6829c3e59deSTodor Tomov for (i = 0; i < camss->ispif.line_num; i++) 6839c3e59deSTodor Tomov for (k = 0; k < camss->vfe_num; k++) 6849c3e59deSTodor Tomov for (j = 0; j < ARRAY_SIZE(camss->vfe[k].line); j++) { 685ec6859b2STodor Tomov ret = media_create_pad_link( 686ec6859b2STodor Tomov &camss->ispif.line[i].subdev.entity, 687ec6859b2STodor Tomov MSM_ISPIF_PAD_SRC, 6889c3e59deSTodor Tomov &camss->vfe[k].line[j].subdev.entity, 689ec6859b2STodor Tomov MSM_VFE_PAD_SINK, 690ec6859b2STodor Tomov 0); 691ec6859b2STodor Tomov if (ret < 0) { 692ec6859b2STodor Tomov dev_err(camss->dev, 693ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 694ec6859b2STodor Tomov camss->ispif.line[i].subdev.entity.name, 6959c3e59deSTodor Tomov camss->vfe[k].line[j].subdev.entity.name, 696ec6859b2STodor Tomov ret); 697ec6859b2STodor Tomov goto err_link; 698ec6859b2STodor Tomov } 699ec6859b2STodor Tomov } 700ec6859b2STodor Tomov 701ec6859b2STodor Tomov return 0; 702ec6859b2STodor Tomov 703ec6859b2STodor Tomov err_link: 7049c3e59deSTodor Tomov i = camss->vfe_num; 705ec6859b2STodor Tomov err_reg_vfe: 7069c3e59deSTodor Tomov for (i--; i >= 0; i--) 7079c3e59deSTodor Tomov msm_vfe_unregister_entities(&camss->vfe[i]); 7089c3e59deSTodor Tomov 709ec6859b2STodor Tomov msm_ispif_unregister_entities(&camss->ispif); 710ec6859b2STodor Tomov err_reg_ispif: 711ec6859b2STodor Tomov 7129c3e59deSTodor Tomov i = camss->csid_num; 713ec6859b2STodor Tomov err_reg_csid: 714ec6859b2STodor Tomov for (i--; i >= 0; i--) 715ec6859b2STodor Tomov msm_csid_unregister_entity(&camss->csid[i]); 716ec6859b2STodor Tomov 7179c3e59deSTodor Tomov i = camss->csiphy_num; 718ec6859b2STodor Tomov err_reg_csiphy: 719ec6859b2STodor Tomov for (i--; i >= 0; i--) 720ec6859b2STodor Tomov msm_csiphy_unregister_entity(&camss->csiphy[i]); 721ec6859b2STodor Tomov 722ec6859b2STodor Tomov return ret; 723ec6859b2STodor Tomov } 724ec6859b2STodor Tomov 725ec6859b2STodor Tomov /* 726ec6859b2STodor Tomov * camss_unregister_entities - Unregister subdev nodes 727ec6859b2STodor Tomov * @camss: CAMSS device 728ec6859b2STodor Tomov * 729ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 730ec6859b2STodor Tomov */ 731ec6859b2STodor Tomov static void camss_unregister_entities(struct camss *camss) 732ec6859b2STodor Tomov { 733ec6859b2STodor Tomov unsigned int i; 734ec6859b2STodor Tomov 7359c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) 736ec6859b2STodor Tomov msm_csiphy_unregister_entity(&camss->csiphy[i]); 737ec6859b2STodor Tomov 7389c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) 739ec6859b2STodor Tomov msm_csid_unregister_entity(&camss->csid[i]); 740ec6859b2STodor Tomov 741ec6859b2STodor Tomov msm_ispif_unregister_entities(&camss->ispif); 7429c3e59deSTodor Tomov 7439c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) 7449c3e59deSTodor Tomov msm_vfe_unregister_entities(&camss->vfe[i]); 745ec6859b2STodor Tomov } 746ec6859b2STodor Tomov 747ec6859b2STodor Tomov static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async, 748ec6859b2STodor Tomov struct v4l2_subdev *subdev, 749ec6859b2STodor Tomov struct v4l2_async_subdev *asd) 750ec6859b2STodor Tomov { 751ec6859b2STodor Tomov struct camss *camss = container_of(async, struct camss, notifier); 752ec6859b2STodor Tomov struct camss_async_subdev *csd = 753ec6859b2STodor Tomov container_of(asd, struct camss_async_subdev, asd); 754ec6859b2STodor Tomov u8 id = csd->interface.csiphy_id; 755ec6859b2STodor Tomov struct csiphy_device *csiphy = &camss->csiphy[id]; 756ec6859b2STodor Tomov 757ec6859b2STodor Tomov csiphy->cfg.csi2 = &csd->interface.csi2; 758ec6859b2STodor Tomov subdev->host_priv = csiphy; 759ec6859b2STodor Tomov 760ec6859b2STodor Tomov return 0; 761ec6859b2STodor Tomov } 762ec6859b2STodor Tomov 763ec6859b2STodor Tomov static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async) 764ec6859b2STodor Tomov { 765ec6859b2STodor Tomov struct camss *camss = container_of(async, struct camss, notifier); 766ec6859b2STodor Tomov struct v4l2_device *v4l2_dev = &camss->v4l2_dev; 767ec6859b2STodor Tomov struct v4l2_subdev *sd; 768ec6859b2STodor Tomov int ret; 769ec6859b2STodor Tomov 770ec6859b2STodor Tomov list_for_each_entry(sd, &v4l2_dev->subdevs, list) { 771ec6859b2STodor Tomov if (sd->host_priv) { 772ec6859b2STodor Tomov struct media_entity *sensor = &sd->entity; 773ec6859b2STodor Tomov struct csiphy_device *csiphy = 774ec6859b2STodor Tomov (struct csiphy_device *) sd->host_priv; 775ec6859b2STodor Tomov struct media_entity *input = &csiphy->subdev.entity; 776ec6859b2STodor Tomov unsigned int i; 777ec6859b2STodor Tomov 778ec6859b2STodor Tomov for (i = 0; i < sensor->num_pads; i++) { 779ec6859b2STodor Tomov if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE) 780ec6859b2STodor Tomov break; 781ec6859b2STodor Tomov } 782ec6859b2STodor Tomov if (i == sensor->num_pads) { 783ec6859b2STodor Tomov dev_err(camss->dev, 784ec6859b2STodor Tomov "No source pad in external entity\n"); 785ec6859b2STodor Tomov return -EINVAL; 786ec6859b2STodor Tomov } 787ec6859b2STodor Tomov 788ec6859b2STodor Tomov ret = media_create_pad_link(sensor, i, 789ec6859b2STodor Tomov input, MSM_CSIPHY_PAD_SINK, 790ec6859b2STodor Tomov MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); 791ec6859b2STodor Tomov if (ret < 0) { 792ec6859b2STodor Tomov dev_err(camss->dev, 793ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 794ec6859b2STodor Tomov sensor->name, input->name, ret); 795ec6859b2STodor Tomov return ret; 796ec6859b2STodor Tomov } 797ec6859b2STodor Tomov } 798ec6859b2STodor Tomov } 799ec6859b2STodor Tomov 800ec6859b2STodor Tomov ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); 801ec6859b2STodor Tomov if (ret < 0) 802ec6859b2STodor Tomov return ret; 803ec6859b2STodor Tomov 804ec6859b2STodor Tomov return media_device_register(&camss->media_dev); 805ec6859b2STodor Tomov } 806ec6859b2STodor Tomov 807ec6859b2STodor Tomov static const struct v4l2_async_notifier_operations camss_subdev_notifier_ops = { 808ec6859b2STodor Tomov .bound = camss_subdev_notifier_bound, 809ec6859b2STodor Tomov .complete = camss_subdev_notifier_complete, 810ec6859b2STodor Tomov }; 811ec6859b2STodor Tomov 812ec6859b2STodor Tomov static const struct media_device_ops camss_media_ops = { 813ec6859b2STodor Tomov .link_notify = v4l2_pipeline_link_notify, 814ec6859b2STodor Tomov }; 815ec6859b2STodor Tomov 816ec6859b2STodor Tomov /* 817ec6859b2STodor Tomov * camss_probe - Probe CAMSS platform device 818ec6859b2STodor Tomov * @pdev: Pointer to CAMSS platform device 819ec6859b2STodor Tomov * 820ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 821ec6859b2STodor Tomov */ 822ec6859b2STodor Tomov static int camss_probe(struct platform_device *pdev) 823ec6859b2STodor Tomov { 824ec6859b2STodor Tomov struct device *dev = &pdev->dev; 825ec6859b2STodor Tomov struct camss *camss; 826ec6859b2STodor Tomov int ret; 827ec6859b2STodor Tomov 828ec6859b2STodor Tomov camss = kzalloc(sizeof(*camss), GFP_KERNEL); 829ec6859b2STodor Tomov if (!camss) 830ec6859b2STodor Tomov return -ENOMEM; 831ec6859b2STodor Tomov 832ec6859b2STodor Tomov atomic_set(&camss->ref_count, 0); 833ec6859b2STodor Tomov camss->dev = dev; 834ec6859b2STodor Tomov platform_set_drvdata(pdev, camss); 835ec6859b2STodor Tomov 8369c3e59deSTodor Tomov if (of_device_is_compatible(dev->of_node, "qcom,msm8916-camss")) { 8379c3e59deSTodor Tomov camss->version = CAMSS_8x16; 8389c3e59deSTodor Tomov camss->csiphy_num = 2; 8399c3e59deSTodor Tomov camss->csid_num = 2; 8409c3e59deSTodor Tomov camss->vfe_num = 1; 8419c3e59deSTodor Tomov } else if (of_device_is_compatible(dev->of_node, 8429c3e59deSTodor Tomov "qcom,msm8996-camss")) { 8439c3e59deSTodor Tomov camss->version = CAMSS_8x96; 8449c3e59deSTodor Tomov camss->csiphy_num = 3; 8459c3e59deSTodor Tomov camss->csid_num = 4; 8469c3e59deSTodor Tomov camss->vfe_num = 2; 8479c3e59deSTodor Tomov } else { 8489c3e59deSTodor Tomov return -EINVAL; 8499c3e59deSTodor Tomov } 8509c3e59deSTodor Tomov 85155b51899STodor Tomov camss->csiphy = devm_kcalloc(dev, camss->csiphy_num, 85255b51899STodor Tomov sizeof(*camss->csiphy), GFP_KERNEL); 8539c3e59deSTodor Tomov if (!camss->csiphy) 8549c3e59deSTodor Tomov return -ENOMEM; 8559c3e59deSTodor Tomov 85655b51899STodor Tomov camss->csid = devm_kcalloc(dev, camss->csid_num, sizeof(*camss->csid), 8579c3e59deSTodor Tomov GFP_KERNEL); 8589c3e59deSTodor Tomov if (!camss->csid) 8599c3e59deSTodor Tomov return -ENOMEM; 8609c3e59deSTodor Tomov 86155b51899STodor Tomov camss->vfe = devm_kcalloc(dev, camss->vfe_num, sizeof(*camss->vfe), 86255b51899STodor Tomov GFP_KERNEL); 8639c3e59deSTodor Tomov if (!camss->vfe) 8649c3e59deSTodor Tomov return -ENOMEM; 8659c3e59deSTodor Tomov 866ec6859b2STodor Tomov ret = camss_of_parse_ports(dev, &camss->notifier); 867ec6859b2STodor Tomov if (ret < 0) 868ec6859b2STodor Tomov return ret; 869ec6859b2STodor Tomov 870ec6859b2STodor Tomov ret = camss_init_subdevices(camss); 871ec6859b2STodor Tomov if (ret < 0) 872ec6859b2STodor Tomov return ret; 873ec6859b2STodor Tomov 874ec6859b2STodor Tomov ret = dma_set_mask_and_coherent(dev, 0xffffffff); 875ec6859b2STodor Tomov if (ret) 876ec6859b2STodor Tomov return ret; 877ec6859b2STodor Tomov 878ec6859b2STodor Tomov camss->media_dev.dev = camss->dev; 879c0decac1SMauro Carvalho Chehab strscpy(camss->media_dev.model, "Qualcomm Camera Subsystem", 880ec6859b2STodor Tomov sizeof(camss->media_dev.model)); 881ec6859b2STodor Tomov camss->media_dev.ops = &camss_media_ops; 882ec6859b2STodor Tomov media_device_init(&camss->media_dev); 883ec6859b2STodor Tomov 884ec6859b2STodor Tomov camss->v4l2_dev.mdev = &camss->media_dev; 885ec6859b2STodor Tomov ret = v4l2_device_register(camss->dev, &camss->v4l2_dev); 886ec6859b2STodor Tomov if (ret < 0) { 887ec6859b2STodor Tomov dev_err(dev, "Failed to register V4L2 device: %d\n", ret); 888ec6859b2STodor Tomov return ret; 889ec6859b2STodor Tomov } 890ec6859b2STodor Tomov 891ec6859b2STodor Tomov ret = camss_register_entities(camss); 892ec6859b2STodor Tomov if (ret < 0) 893ec6859b2STodor Tomov goto err_register_entities; 894ec6859b2STodor Tomov 895ec6859b2STodor Tomov if (camss->notifier.num_subdevs) { 896ec6859b2STodor Tomov camss->notifier.ops = &camss_subdev_notifier_ops; 897ec6859b2STodor Tomov 898ec6859b2STodor Tomov ret = v4l2_async_notifier_register(&camss->v4l2_dev, 899ec6859b2STodor Tomov &camss->notifier); 900ec6859b2STodor Tomov if (ret) { 901ec6859b2STodor Tomov dev_err(dev, 902ec6859b2STodor Tomov "Failed to register async subdev nodes: %d\n", 903ec6859b2STodor Tomov ret); 904ec6859b2STodor Tomov goto err_register_subdevs; 905ec6859b2STodor Tomov } 906ec6859b2STodor Tomov } else { 907ec6859b2STodor Tomov ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); 908ec6859b2STodor Tomov if (ret < 0) { 909ec6859b2STodor Tomov dev_err(dev, "Failed to register subdev nodes: %d\n", 910ec6859b2STodor Tomov ret); 911ec6859b2STodor Tomov goto err_register_subdevs; 912ec6859b2STodor Tomov } 913ec6859b2STodor Tomov 914ec6859b2STodor Tomov ret = media_device_register(&camss->media_dev); 915ec6859b2STodor Tomov if (ret < 0) { 916ec6859b2STodor Tomov dev_err(dev, "Failed to register media device: %d\n", 917ec6859b2STodor Tomov ret); 918ec6859b2STodor Tomov goto err_register_subdevs; 919ec6859b2STodor Tomov } 920ec6859b2STodor Tomov } 921ec6859b2STodor Tomov 92202afa816STodor Tomov if (camss->version == CAMSS_8x96) { 92302afa816STodor Tomov camss->genpd[PM_DOMAIN_VFE0] = dev_pm_domain_attach_by_id( 92402afa816STodor Tomov camss->dev, PM_DOMAIN_VFE0); 92502afa816STodor Tomov if (IS_ERR(camss->genpd[PM_DOMAIN_VFE0])) 92602afa816STodor Tomov return PTR_ERR(camss->genpd[PM_DOMAIN_VFE0]); 92702afa816STodor Tomov 92802afa816STodor Tomov camss->genpd[PM_DOMAIN_VFE1] = dev_pm_domain_attach_by_id( 92902afa816STodor Tomov camss->dev, PM_DOMAIN_VFE1); 93002afa816STodor Tomov if (IS_ERR(camss->genpd[PM_DOMAIN_VFE1])) { 93102afa816STodor Tomov dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], 93202afa816STodor Tomov true); 93302afa816STodor Tomov return PTR_ERR(camss->genpd[PM_DOMAIN_VFE1]); 93402afa816STodor Tomov } 93502afa816STodor Tomov } 93602afa816STodor Tomov 93702afa816STodor Tomov pm_runtime_enable(dev); 93802afa816STodor Tomov 939ec6859b2STodor Tomov return 0; 940ec6859b2STodor Tomov 941ec6859b2STodor Tomov err_register_subdevs: 942ec6859b2STodor Tomov camss_unregister_entities(camss); 943ec6859b2STodor Tomov err_register_entities: 944ec6859b2STodor Tomov v4l2_device_unregister(&camss->v4l2_dev); 945ec6859b2STodor Tomov 946ec6859b2STodor Tomov return ret; 947ec6859b2STodor Tomov } 948ec6859b2STodor Tomov 949ec6859b2STodor Tomov void camss_delete(struct camss *camss) 950ec6859b2STodor Tomov { 951ec6859b2STodor Tomov v4l2_device_unregister(&camss->v4l2_dev); 952ec6859b2STodor Tomov media_device_unregister(&camss->media_dev); 953ec6859b2STodor Tomov media_device_cleanup(&camss->media_dev); 954ec6859b2STodor Tomov 95502afa816STodor Tomov pm_runtime_disable(camss->dev); 95602afa816STodor Tomov 95702afa816STodor Tomov if (camss->version == CAMSS_8x96) { 95802afa816STodor Tomov dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true); 95902afa816STodor Tomov dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true); 96002afa816STodor Tomov } 96102afa816STodor Tomov 962ec6859b2STodor Tomov kfree(camss); 963ec6859b2STodor Tomov } 964ec6859b2STodor Tomov 965ec6859b2STodor Tomov /* 966ec6859b2STodor Tomov * camss_remove - Remove CAMSS platform device 967ec6859b2STodor Tomov * @pdev: Pointer to CAMSS platform device 968ec6859b2STodor Tomov * 969ec6859b2STodor Tomov * Always returns 0. 970ec6859b2STodor Tomov */ 971ec6859b2STodor Tomov static int camss_remove(struct platform_device *pdev) 972ec6859b2STodor Tomov { 9739c3e59deSTodor Tomov unsigned int i; 9749c3e59deSTodor Tomov 975ec6859b2STodor Tomov struct camss *camss = platform_get_drvdata(pdev); 976ec6859b2STodor Tomov 9779c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) 9789c3e59deSTodor Tomov msm_vfe_stop_streaming(&camss->vfe[i]); 979ec6859b2STodor Tomov 980ec6859b2STodor Tomov v4l2_async_notifier_unregister(&camss->notifier); 981ec6859b2STodor Tomov camss_unregister_entities(camss); 982ec6859b2STodor Tomov 983ec6859b2STodor Tomov if (atomic_read(&camss->ref_count) == 0) 984ec6859b2STodor Tomov camss_delete(camss); 985ec6859b2STodor Tomov 986ec6859b2STodor Tomov return 0; 987ec6859b2STodor Tomov } 988ec6859b2STodor Tomov 989ec6859b2STodor Tomov static const struct of_device_id camss_dt_match[] = { 990ec6859b2STodor Tomov { .compatible = "qcom,msm8916-camss" }, 9919c3e59deSTodor Tomov { .compatible = "qcom,msm8996-camss" }, 992ec6859b2STodor Tomov { } 993ec6859b2STodor Tomov }; 994ec6859b2STodor Tomov 995ec6859b2STodor Tomov MODULE_DEVICE_TABLE(of, camss_dt_match); 996ec6859b2STodor Tomov 99744a9ffd4SArnd Bergmann static int __maybe_unused camss_runtime_suspend(struct device *dev) 99802afa816STodor Tomov { 99902afa816STodor Tomov return 0; 100002afa816STodor Tomov } 100102afa816STodor Tomov 100244a9ffd4SArnd Bergmann static int __maybe_unused camss_runtime_resume(struct device *dev) 100302afa816STodor Tomov { 100402afa816STodor Tomov return 0; 100502afa816STodor Tomov } 100602afa816STodor Tomov 100702afa816STodor Tomov static const struct dev_pm_ops camss_pm_ops = { 100802afa816STodor Tomov SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 100902afa816STodor Tomov pm_runtime_force_resume) 101002afa816STodor Tomov SET_RUNTIME_PM_OPS(camss_runtime_suspend, camss_runtime_resume, NULL) 101102afa816STodor Tomov }; 101202afa816STodor Tomov 1013ec6859b2STodor Tomov static struct platform_driver qcom_camss_driver = { 1014ec6859b2STodor Tomov .probe = camss_probe, 1015ec6859b2STodor Tomov .remove = camss_remove, 1016ec6859b2STodor Tomov .driver = { 1017ec6859b2STodor Tomov .name = "qcom-camss", 1018ec6859b2STodor Tomov .of_match_table = camss_dt_match, 101902afa816STodor Tomov .pm = &camss_pm_ops, 1020ec6859b2STodor Tomov }, 1021ec6859b2STodor Tomov }; 1022ec6859b2STodor Tomov 1023ec6859b2STodor Tomov module_platform_driver(qcom_camss_driver); 1024ec6859b2STodor Tomov 1025ec6859b2STodor Tomov MODULE_ALIAS("platform:qcom-camss"); 1026ec6859b2STodor Tomov MODULE_DESCRIPTION("Qualcomm Camera Subsystem driver"); 1027ec6859b2STodor Tomov MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>"); 1028ec6859b2STodor Tomov MODULE_LICENSE("GPL v2"); 1029