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 2869e5d1581SAngeloGioacchino Del Regno static const struct resources csiphy_res_660[] = { 2879e5d1581SAngeloGioacchino Del Regno /* CSIPHY0 */ 2889e5d1581SAngeloGioacchino Del Regno { 2899e5d1581SAngeloGioacchino Del Regno .regulator = { NULL }, 2909e5d1581SAngeloGioacchino Del Regno .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer", 2919e5d1581SAngeloGioacchino Del Regno "csi0_phy", "csiphy_ahb2crif" }, 2929e5d1581SAngeloGioacchino Del Regno .clock_rate = { { 0 }, 2939e5d1581SAngeloGioacchino Del Regno { 0 }, 2949e5d1581SAngeloGioacchino Del Regno { 0 }, 2959e5d1581SAngeloGioacchino Del Regno { 100000000, 200000000, 269333333 }, 2969e5d1581SAngeloGioacchino Del Regno { 0 } }, 2979e5d1581SAngeloGioacchino Del Regno .reg = { "csiphy0", "csiphy0_clk_mux" }, 2989e5d1581SAngeloGioacchino Del Regno .interrupt = { "csiphy0" } 2999e5d1581SAngeloGioacchino Del Regno }, 3009e5d1581SAngeloGioacchino Del Regno 3019e5d1581SAngeloGioacchino Del Regno /* CSIPHY1 */ 3029e5d1581SAngeloGioacchino Del Regno { 3039e5d1581SAngeloGioacchino Del Regno .regulator = { NULL }, 3049e5d1581SAngeloGioacchino Del Regno .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer", 3059e5d1581SAngeloGioacchino Del Regno "csi1_phy", "csiphy_ahb2crif" }, 3069e5d1581SAngeloGioacchino Del Regno .clock_rate = { { 0 }, 3079e5d1581SAngeloGioacchino Del Regno { 0 }, 3089e5d1581SAngeloGioacchino Del Regno { 0 }, 3099e5d1581SAngeloGioacchino Del Regno { 100000000, 200000000, 269333333 }, 3109e5d1581SAngeloGioacchino Del Regno { 0 } }, 3119e5d1581SAngeloGioacchino Del Regno .reg = { "csiphy1", "csiphy1_clk_mux" }, 3129e5d1581SAngeloGioacchino Del Regno .interrupt = { "csiphy1" } 3139e5d1581SAngeloGioacchino Del Regno }, 3149e5d1581SAngeloGioacchino Del Regno 3159e5d1581SAngeloGioacchino Del Regno /* CSIPHY2 */ 3169e5d1581SAngeloGioacchino Del Regno { 3179e5d1581SAngeloGioacchino Del Regno .regulator = { NULL }, 3189e5d1581SAngeloGioacchino Del Regno .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy2_timer", 3199e5d1581SAngeloGioacchino Del Regno "csi2_phy", "csiphy_ahb2crif" }, 3209e5d1581SAngeloGioacchino Del Regno .clock_rate = { { 0 }, 3219e5d1581SAngeloGioacchino Del Regno { 0 }, 3229e5d1581SAngeloGioacchino Del Regno { 0 }, 3239e5d1581SAngeloGioacchino Del Regno { 100000000, 200000000, 269333333 }, 3249e5d1581SAngeloGioacchino Del Regno { 0 } }, 3259e5d1581SAngeloGioacchino Del Regno .reg = { "csiphy2", "csiphy2_clk_mux" }, 3269e5d1581SAngeloGioacchino Del Regno .interrupt = { "csiphy2" } 3279e5d1581SAngeloGioacchino Del Regno } 3289e5d1581SAngeloGioacchino Del Regno }; 3299e5d1581SAngeloGioacchino Del Regno 3309e5d1581SAngeloGioacchino Del Regno static const struct resources csid_res_660[] = { 3319e5d1581SAngeloGioacchino Del Regno /* CSID0 */ 3329e5d1581SAngeloGioacchino Del Regno { 3339e5d1581SAngeloGioacchino Del Regno .regulator = { "vdda", "vdd_sec" }, 3349e5d1581SAngeloGioacchino Del Regno .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", 3359e5d1581SAngeloGioacchino Del Regno "csi0", "csi0_phy", "csi0_pix", "csi0_rdi", 3369e5d1581SAngeloGioacchino Del Regno "cphy_csid0" }, 3379e5d1581SAngeloGioacchino Del Regno .clock_rate = { { 0 }, 3389e5d1581SAngeloGioacchino Del Regno { 0 }, 3399e5d1581SAngeloGioacchino Del Regno { 0 }, 3409e5d1581SAngeloGioacchino Del Regno { 0 }, 3419e5d1581SAngeloGioacchino Del Regno { 100000000, 200000000, 310000000, 3429e5d1581SAngeloGioacchino Del Regno 404000000, 465000000 }, 3439e5d1581SAngeloGioacchino Del Regno { 0 }, 3449e5d1581SAngeloGioacchino Del Regno { 0 }, 3459e5d1581SAngeloGioacchino Del Regno { 0 }, 3469e5d1581SAngeloGioacchino Del Regno { 0 } }, 3479e5d1581SAngeloGioacchino Del Regno .reg = { "csid0" }, 3489e5d1581SAngeloGioacchino Del Regno .interrupt = { "csid0" } 3499e5d1581SAngeloGioacchino Del Regno }, 3509e5d1581SAngeloGioacchino Del Regno 3519e5d1581SAngeloGioacchino Del Regno /* CSID1 */ 3529e5d1581SAngeloGioacchino Del Regno { 3539e5d1581SAngeloGioacchino Del Regno .regulator = { "vdda", "vdd_sec" }, 3549e5d1581SAngeloGioacchino Del Regno .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", 3559e5d1581SAngeloGioacchino Del Regno "csi1", "csi1_phy", "csi1_pix", "csi1_rdi", 3569e5d1581SAngeloGioacchino Del Regno "cphy_csid1" }, 3579e5d1581SAngeloGioacchino Del Regno .clock_rate = { { 0 }, 3589e5d1581SAngeloGioacchino Del Regno { 0 }, 3599e5d1581SAngeloGioacchino Del Regno { 0 }, 3609e5d1581SAngeloGioacchino Del Regno { 0 }, 3619e5d1581SAngeloGioacchino Del Regno { 100000000, 200000000, 310000000, 3629e5d1581SAngeloGioacchino Del Regno 404000000, 465000000 }, 3639e5d1581SAngeloGioacchino Del Regno { 0 }, 3649e5d1581SAngeloGioacchino Del Regno { 0 }, 3659e5d1581SAngeloGioacchino Del Regno { 0 }, 3669e5d1581SAngeloGioacchino Del Regno { 0 } }, 3679e5d1581SAngeloGioacchino Del Regno .reg = { "csid1" }, 3689e5d1581SAngeloGioacchino Del Regno .interrupt = { "csid1" } 3699e5d1581SAngeloGioacchino Del Regno }, 3709e5d1581SAngeloGioacchino Del Regno 3719e5d1581SAngeloGioacchino Del Regno /* CSID2 */ 3729e5d1581SAngeloGioacchino Del Regno { 3739e5d1581SAngeloGioacchino Del Regno .regulator = { "vdda", "vdd_sec" }, 3749e5d1581SAngeloGioacchino Del Regno .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb", 3759e5d1581SAngeloGioacchino Del Regno "csi2", "csi2_phy", "csi2_pix", "csi2_rdi", 3769e5d1581SAngeloGioacchino Del Regno "cphy_csid2" }, 3779e5d1581SAngeloGioacchino Del Regno .clock_rate = { { 0 }, 3789e5d1581SAngeloGioacchino Del Regno { 0 }, 3799e5d1581SAngeloGioacchino Del Regno { 0 }, 3809e5d1581SAngeloGioacchino Del Regno { 0 }, 3819e5d1581SAngeloGioacchino Del Regno { 100000000, 200000000, 310000000, 3829e5d1581SAngeloGioacchino Del Regno 404000000, 465000000 }, 3839e5d1581SAngeloGioacchino Del Regno { 0 }, 3849e5d1581SAngeloGioacchino Del Regno { 0 }, 3859e5d1581SAngeloGioacchino Del Regno { 0 }, 3869e5d1581SAngeloGioacchino Del Regno { 0 } }, 3879e5d1581SAngeloGioacchino Del Regno .reg = { "csid2" }, 3889e5d1581SAngeloGioacchino Del Regno .interrupt = { "csid2" } 3899e5d1581SAngeloGioacchino Del Regno }, 3909e5d1581SAngeloGioacchino Del Regno 3919e5d1581SAngeloGioacchino Del Regno /* CSID3 */ 3929e5d1581SAngeloGioacchino Del Regno { 3939e5d1581SAngeloGioacchino Del Regno .regulator = { "vdda", "vdd_sec" }, 3949e5d1581SAngeloGioacchino Del Regno .clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb", 3959e5d1581SAngeloGioacchino Del Regno "csi3", "csi3_phy", "csi3_pix", "csi3_rdi", 3969e5d1581SAngeloGioacchino Del Regno "cphy_csid3" }, 3979e5d1581SAngeloGioacchino Del Regno .clock_rate = { { 0 }, 3989e5d1581SAngeloGioacchino Del Regno { 0 }, 3999e5d1581SAngeloGioacchino Del Regno { 0 }, 4009e5d1581SAngeloGioacchino Del Regno { 0 }, 4019e5d1581SAngeloGioacchino Del Regno { 100000000, 200000000, 310000000, 4029e5d1581SAngeloGioacchino Del Regno 404000000, 465000000 }, 4039e5d1581SAngeloGioacchino Del Regno { 0 }, 4049e5d1581SAngeloGioacchino Del Regno { 0 }, 4059e5d1581SAngeloGioacchino Del Regno { 0 }, 4069e5d1581SAngeloGioacchino Del Regno { 0 } }, 4079e5d1581SAngeloGioacchino Del Regno .reg = { "csid3" }, 4089e5d1581SAngeloGioacchino Del Regno .interrupt = { "csid3" } 4099e5d1581SAngeloGioacchino Del Regno } 4109e5d1581SAngeloGioacchino Del Regno }; 4119e5d1581SAngeloGioacchino Del Regno 4129e5d1581SAngeloGioacchino Del Regno static const struct resources_ispif ispif_res_660 = { 4139e5d1581SAngeloGioacchino Del Regno /* ISPIF */ 4149e5d1581SAngeloGioacchino Del Regno .clock = { "top_ahb", "ahb", "ispif_ahb", 4159e5d1581SAngeloGioacchino Del Regno "csi0", "csi0_pix", "csi0_rdi", 4169e5d1581SAngeloGioacchino Del Regno "csi1", "csi1_pix", "csi1_rdi", 4179e5d1581SAngeloGioacchino Del Regno "csi2", "csi2_pix", "csi2_rdi", 4189e5d1581SAngeloGioacchino Del Regno "csi3", "csi3_pix", "csi3_rdi" }, 4199e5d1581SAngeloGioacchino Del Regno .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" }, 4209e5d1581SAngeloGioacchino Del Regno .reg = { "ispif", "csi_clk_mux" }, 4219e5d1581SAngeloGioacchino Del Regno .interrupt = "ispif" 4229e5d1581SAngeloGioacchino Del Regno }; 4239e5d1581SAngeloGioacchino Del Regno 4249e5d1581SAngeloGioacchino Del Regno static const struct resources vfe_res_660[] = { 4259e5d1581SAngeloGioacchino Del Regno /* VFE0 */ 4269e5d1581SAngeloGioacchino Del Regno { 4279e5d1581SAngeloGioacchino Del Regno .regulator = { NULL }, 4289e5d1581SAngeloGioacchino Del Regno .clock = { "throttle_axi", "top_ahb", "ahb", "vfe0", 4299e5d1581SAngeloGioacchino Del Regno "csi_vfe0", "vfe_ahb", "vfe0_ahb", "vfe_axi", 4309e5d1581SAngeloGioacchino Del Regno "vfe0_stream"}, 4319e5d1581SAngeloGioacchino Del Regno .clock_rate = { { 0 }, 4329e5d1581SAngeloGioacchino Del Regno { 0 }, 4339e5d1581SAngeloGioacchino Del Regno { 0 }, 4349e5d1581SAngeloGioacchino Del Regno { 120000000, 200000000, 256000000, 4359e5d1581SAngeloGioacchino Del Regno 300000000, 404000000, 480000000, 4369e5d1581SAngeloGioacchino Del Regno 540000000, 576000000 }, 4379e5d1581SAngeloGioacchino Del Regno { 0 }, 4389e5d1581SAngeloGioacchino Del Regno { 0 }, 4399e5d1581SAngeloGioacchino Del Regno { 0 }, 4409e5d1581SAngeloGioacchino Del Regno { 0 }, 4419e5d1581SAngeloGioacchino Del Regno { 0 } }, 4429e5d1581SAngeloGioacchino Del Regno .reg = { "vfe0" }, 4439e5d1581SAngeloGioacchino Del Regno .interrupt = { "vfe0" } 4449e5d1581SAngeloGioacchino Del Regno }, 4459e5d1581SAngeloGioacchino Del Regno 4469e5d1581SAngeloGioacchino Del Regno /* VFE1 */ 4479e5d1581SAngeloGioacchino Del Regno { 4489e5d1581SAngeloGioacchino Del Regno .regulator = { NULL }, 4499e5d1581SAngeloGioacchino Del Regno .clock = { "throttle_axi", "top_ahb", "ahb", "vfe1", 4509e5d1581SAngeloGioacchino Del Regno "csi_vfe1", "vfe_ahb", "vfe1_ahb", "vfe_axi", 4519e5d1581SAngeloGioacchino Del Regno "vfe1_stream"}, 4529e5d1581SAngeloGioacchino Del Regno .clock_rate = { { 0 }, 4539e5d1581SAngeloGioacchino Del Regno { 0 }, 4549e5d1581SAngeloGioacchino Del Regno { 0 }, 4559e5d1581SAngeloGioacchino Del Regno { 120000000, 200000000, 256000000, 4569e5d1581SAngeloGioacchino Del Regno 300000000, 404000000, 480000000, 4579e5d1581SAngeloGioacchino Del Regno 540000000, 576000000 }, 4589e5d1581SAngeloGioacchino Del Regno { 0 }, 4599e5d1581SAngeloGioacchino Del Regno { 0 }, 4609e5d1581SAngeloGioacchino Del Regno { 0 }, 4619e5d1581SAngeloGioacchino Del Regno { 0 }, 4629e5d1581SAngeloGioacchino Del Regno { 0 } }, 4639e5d1581SAngeloGioacchino Del Regno .reg = { "vfe1" }, 4649e5d1581SAngeloGioacchino Del Regno .interrupt = { "vfe1" } 4659e5d1581SAngeloGioacchino Del Regno } 4669e5d1581SAngeloGioacchino Del Regno }; 4679e5d1581SAngeloGioacchino Del Regno 4682f8b6719SRobert Foss static const struct resources csiphy_res_845[] = { 4692f8b6719SRobert Foss /* CSIPHY0 */ 4702f8b6719SRobert Foss { 4712f8b6719SRobert Foss .regulator = { NULL }, 4722f8b6719SRobert Foss .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src", 4732f8b6719SRobert Foss "cpas_ahb", "cphy_rx_src", "csiphy0", 4742f8b6719SRobert Foss "csiphy0_timer_src", "csiphy0_timer" }, 4752f8b6719SRobert Foss .clock_rate = { { 0 }, 4762f8b6719SRobert Foss { 0 }, 4772f8b6719SRobert Foss { 0 }, 4782f8b6719SRobert Foss { 0 }, 4792f8b6719SRobert Foss { 0 }, 4802f8b6719SRobert Foss { 0 }, 4812f8b6719SRobert Foss { 0 }, 4822f8b6719SRobert Foss { 19200000, 240000000, 269333333 } }, 4832f8b6719SRobert Foss .reg = { "csiphy0" }, 4842f8b6719SRobert Foss .interrupt = { "csiphy0" } 4852f8b6719SRobert Foss }, 4862f8b6719SRobert Foss 4872f8b6719SRobert Foss /* CSIPHY1 */ 4882f8b6719SRobert Foss { 4892f8b6719SRobert Foss .regulator = { NULL }, 4902f8b6719SRobert Foss .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src", 4912f8b6719SRobert Foss "cpas_ahb", "cphy_rx_src", "csiphy1", 4922f8b6719SRobert Foss "csiphy1_timer_src", "csiphy1_timer" }, 4932f8b6719SRobert Foss .clock_rate = { { 0 }, 4942f8b6719SRobert Foss { 0 }, 4952f8b6719SRobert Foss { 0 }, 4962f8b6719SRobert Foss { 0 }, 4972f8b6719SRobert Foss { 0 }, 4982f8b6719SRobert Foss { 0 }, 4992f8b6719SRobert Foss { 0 }, 5002f8b6719SRobert Foss { 19200000, 240000000, 269333333 } }, 5012f8b6719SRobert Foss .reg = { "csiphy1" }, 5022f8b6719SRobert Foss .interrupt = { "csiphy1" } 5032f8b6719SRobert Foss }, 5042f8b6719SRobert Foss 5052f8b6719SRobert Foss /* CSIPHY2 */ 5062f8b6719SRobert Foss { 5072f8b6719SRobert Foss .regulator = { NULL }, 5082f8b6719SRobert Foss .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src", 5092f8b6719SRobert Foss "cpas_ahb", "cphy_rx_src", "csiphy2", 5102f8b6719SRobert Foss "csiphy2_timer_src", "csiphy2_timer" }, 5112f8b6719SRobert Foss .clock_rate = { { 0 }, 5122f8b6719SRobert Foss { 0 }, 5132f8b6719SRobert Foss { 0 }, 5142f8b6719SRobert Foss { 0 }, 5152f8b6719SRobert Foss { 0 }, 5162f8b6719SRobert Foss { 0 }, 5172f8b6719SRobert Foss { 0 }, 5182f8b6719SRobert Foss { 19200000, 240000000, 269333333 } }, 5192f8b6719SRobert Foss .reg = { "csiphy2" }, 5202f8b6719SRobert Foss .interrupt = { "csiphy2" } 5212f8b6719SRobert Foss }, 5222f8b6719SRobert Foss 5232f8b6719SRobert Foss /* CSIPHY3 */ 5242f8b6719SRobert Foss { 5252f8b6719SRobert Foss .regulator = { NULL }, 5262f8b6719SRobert Foss .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src", 5272f8b6719SRobert Foss "cpas_ahb", "cphy_rx_src", "csiphy3", 5282f8b6719SRobert Foss "csiphy3_timer_src", "csiphy3_timer" }, 5292f8b6719SRobert Foss .clock_rate = { { 0 }, 5302f8b6719SRobert Foss { 0 }, 5312f8b6719SRobert Foss { 0 }, 5322f8b6719SRobert Foss { 0 }, 5332f8b6719SRobert Foss { 0 }, 5342f8b6719SRobert Foss { 0 }, 5352f8b6719SRobert Foss { 0 }, 5362f8b6719SRobert Foss { 19200000, 240000000, 269333333 } }, 5372f8b6719SRobert Foss .reg = { "csiphy3" }, 5382f8b6719SRobert Foss .interrupt = { "csiphy3" } 5392f8b6719SRobert Foss } 5402f8b6719SRobert Foss }; 5412f8b6719SRobert Foss 542eebe6d00SRobert Foss static const struct resources csid_res_845[] = { 543eebe6d00SRobert Foss /* CSID0 */ 544eebe6d00SRobert Foss { 545eebe6d00SRobert Foss .regulator = { "vdda-csi0" }, 546eebe6d00SRobert Foss .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src", 547eebe6d00SRobert Foss "soc_ahb", "vfe0", "vfe0_src", 548eebe6d00SRobert Foss "vfe0_cphy_rx", "csi0", 549eebe6d00SRobert Foss "csi0_src" }, 550eebe6d00SRobert Foss .clock_rate = { { 0 }, 551eebe6d00SRobert Foss { 384000000 }, 552eebe6d00SRobert Foss { 80000000 }, 553eebe6d00SRobert Foss { 0 }, 554eebe6d00SRobert Foss { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 }, 555eebe6d00SRobert Foss { 320000000 }, 556eebe6d00SRobert Foss { 0 }, 557eebe6d00SRobert Foss { 19200000, 75000000, 384000000, 538666667 }, 558eebe6d00SRobert Foss { 384000000 } }, 559eebe6d00SRobert Foss .reg = { "csid0" }, 560eebe6d00SRobert Foss .interrupt = { "csid0" } 561eebe6d00SRobert Foss }, 562eebe6d00SRobert Foss 563eebe6d00SRobert Foss /* CSID1 */ 564eebe6d00SRobert Foss { 565eebe6d00SRobert Foss .regulator = { "vdda-csi1" }, 566eebe6d00SRobert Foss .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src", 567eebe6d00SRobert Foss "soc_ahb", "vfe1", "vfe1_src", 568eebe6d00SRobert Foss "vfe1_cphy_rx", "csi1", 569eebe6d00SRobert Foss "csi1_src" }, 570eebe6d00SRobert Foss .clock_rate = { { 0 }, 571eebe6d00SRobert Foss { 384000000 }, 572eebe6d00SRobert Foss { 80000000 }, 573eebe6d00SRobert Foss { 0 }, 574eebe6d00SRobert Foss { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 }, 575eebe6d00SRobert Foss { 320000000 }, 576eebe6d00SRobert Foss { 0 }, 577eebe6d00SRobert Foss { 19200000, 75000000, 384000000, 538666667 }, 578eebe6d00SRobert Foss { 384000000 } }, 579eebe6d00SRobert Foss .reg = { "csid1" }, 580eebe6d00SRobert Foss .interrupt = { "csid1" } 581eebe6d00SRobert Foss }, 582eebe6d00SRobert Foss 583eebe6d00SRobert Foss /* CSID2 */ 584eebe6d00SRobert Foss { 585eebe6d00SRobert Foss .regulator = { "vdda-csi2" }, 586eebe6d00SRobert Foss .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src", 587eebe6d00SRobert Foss "soc_ahb", "vfe_lite", "vfe_lite_src", 588eebe6d00SRobert Foss "vfe_lite_cphy_rx", "csi2", 589eebe6d00SRobert Foss "csi2_src" }, 590eebe6d00SRobert Foss .clock_rate = { { 0 }, 591eebe6d00SRobert Foss { 384000000 }, 592eebe6d00SRobert Foss { 80000000 }, 593eebe6d00SRobert Foss { 0 }, 594eebe6d00SRobert Foss { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 }, 595eebe6d00SRobert Foss { 320000000 }, 596eebe6d00SRobert Foss { 0 }, 597eebe6d00SRobert Foss { 19200000, 75000000, 384000000, 538666667 }, 598eebe6d00SRobert Foss { 384000000 } }, 599eebe6d00SRobert Foss .reg = { "csid2" }, 600eebe6d00SRobert Foss .interrupt = { "csid2" } 601eebe6d00SRobert Foss } 602eebe6d00SRobert Foss }; 603eebe6d00SRobert Foss 6047319cdf1SRobert Foss static const struct resources vfe_res_845[] = { 6057319cdf1SRobert Foss /* VFE0 */ 6067319cdf1SRobert Foss { 6077319cdf1SRobert Foss .regulator = { NULL }, 6087319cdf1SRobert Foss .clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src", 6097319cdf1SRobert Foss "soc_ahb", "vfe0", "vfe0_axi", 6107319cdf1SRobert Foss "vfe0_src", "csi0", 6117319cdf1SRobert Foss "csi0_src"}, 6127319cdf1SRobert Foss .clock_rate = { { 0 }, 6137319cdf1SRobert Foss { 0 }, 6147319cdf1SRobert Foss { 80000000 }, 6157319cdf1SRobert Foss { 0 }, 6167319cdf1SRobert Foss { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 }, 6177319cdf1SRobert Foss { 0 }, 6187319cdf1SRobert Foss { 320000000 }, 6197319cdf1SRobert Foss { 19200000, 75000000, 384000000, 538666667 }, 6207319cdf1SRobert Foss { 384000000 } }, 6217319cdf1SRobert Foss .reg = { "vfe0" }, 6227319cdf1SRobert Foss .interrupt = { "vfe0" } 6237319cdf1SRobert Foss }, 6247319cdf1SRobert Foss 6257319cdf1SRobert Foss /* VFE1 */ 6267319cdf1SRobert Foss { 6277319cdf1SRobert Foss .regulator = { NULL }, 6287319cdf1SRobert Foss .clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src", 6297319cdf1SRobert Foss "soc_ahb", "vfe1", "vfe1_axi", 6307319cdf1SRobert Foss "vfe1_src", "csi1", 6317319cdf1SRobert Foss "csi1_src"}, 6327319cdf1SRobert Foss .clock_rate = { { 0 }, 6337319cdf1SRobert Foss { 0 }, 6347319cdf1SRobert Foss { 80000000 }, 6357319cdf1SRobert Foss { 0 }, 6367319cdf1SRobert Foss { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 }, 6377319cdf1SRobert Foss { 0 }, 6387319cdf1SRobert Foss { 320000000 }, 6397319cdf1SRobert Foss { 19200000, 75000000, 384000000, 538666667 }, 6407319cdf1SRobert Foss { 384000000 } }, 6417319cdf1SRobert Foss .reg = { "vfe1" }, 6427319cdf1SRobert Foss .interrupt = { "vfe1" } 6437319cdf1SRobert Foss }, 6447319cdf1SRobert Foss 6457319cdf1SRobert Foss /* VFE-lite */ 6467319cdf1SRobert Foss { 6477319cdf1SRobert Foss .regulator = { NULL }, 6487319cdf1SRobert Foss .clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src", 6497319cdf1SRobert Foss "soc_ahb", "vfe_lite", 6507319cdf1SRobert Foss "vfe_lite_src", "csi2", 6517319cdf1SRobert Foss "csi2_src"}, 6527319cdf1SRobert Foss .clock_rate = { { 0 }, 6537319cdf1SRobert Foss { 0 }, 6547319cdf1SRobert Foss { 80000000 }, 6557319cdf1SRobert Foss { 0 }, 6567319cdf1SRobert Foss { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 }, 6577319cdf1SRobert Foss { 320000000 }, 6587319cdf1SRobert Foss { 19200000, 75000000, 384000000, 538666667 }, 6597319cdf1SRobert Foss { 384000000 } }, 6607319cdf1SRobert Foss .reg = { "vfe_lite" }, 6617319cdf1SRobert Foss .interrupt = { "vfe_lite" } 6627319cdf1SRobert Foss } 6637319cdf1SRobert Foss }; 6647319cdf1SRobert Foss 665ec6859b2STodor Tomov /* 666ec6859b2STodor Tomov * camss_add_clock_margin - Add margin to clock frequency rate 667ec6859b2STodor Tomov * @rate: Clock frequency rate 668ec6859b2STodor Tomov * 669ec6859b2STodor Tomov * When making calculations with physical clock frequency values 670ec6859b2STodor Tomov * some safety margin must be added. Add it. 671ec6859b2STodor Tomov */ 672ec6859b2STodor Tomov inline void camss_add_clock_margin(u64 *rate) 673ec6859b2STodor Tomov { 674ec6859b2STodor Tomov *rate *= CAMSS_CLOCK_MARGIN_NUMERATOR; 675ec6859b2STodor Tomov *rate = div_u64(*rate, CAMSS_CLOCK_MARGIN_DENOMINATOR); 676ec6859b2STodor Tomov } 677ec6859b2STodor Tomov 678ec6859b2STodor Tomov /* 679ec6859b2STodor Tomov * camss_enable_clocks - Enable multiple clocks 680ec6859b2STodor Tomov * @nclocks: Number of clocks in clock array 681ec6859b2STodor Tomov * @clock: Clock array 682ec6859b2STodor Tomov * @dev: Device 683ec6859b2STodor Tomov * 684ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 685ec6859b2STodor Tomov */ 686ec6859b2STodor Tomov int camss_enable_clocks(int nclocks, struct camss_clock *clock, 687ec6859b2STodor Tomov struct device *dev) 688ec6859b2STodor Tomov { 689ec6859b2STodor Tomov int ret; 690ec6859b2STodor Tomov int i; 691ec6859b2STodor Tomov 692ec6859b2STodor Tomov for (i = 0; i < nclocks; i++) { 693ec6859b2STodor Tomov ret = clk_prepare_enable(clock[i].clk); 694ec6859b2STodor Tomov if (ret) { 695ec6859b2STodor Tomov dev_err(dev, "clock enable failed: %d\n", ret); 696ec6859b2STodor Tomov goto error; 697ec6859b2STodor Tomov } 698ec6859b2STodor Tomov } 699ec6859b2STodor Tomov 700ec6859b2STodor Tomov return 0; 701ec6859b2STodor Tomov 702ec6859b2STodor Tomov error: 703ec6859b2STodor Tomov for (i--; i >= 0; i--) 704ec6859b2STodor Tomov clk_disable_unprepare(clock[i].clk); 705ec6859b2STodor Tomov 706ec6859b2STodor Tomov return ret; 707ec6859b2STodor Tomov } 708ec6859b2STodor Tomov 709ec6859b2STodor Tomov /* 710ec6859b2STodor Tomov * camss_disable_clocks - Disable multiple clocks 711ec6859b2STodor Tomov * @nclocks: Number of clocks in clock array 712ec6859b2STodor Tomov * @clock: Clock array 713ec6859b2STodor Tomov */ 714ec6859b2STodor Tomov void camss_disable_clocks(int nclocks, struct camss_clock *clock) 715ec6859b2STodor Tomov { 716ec6859b2STodor Tomov int i; 717ec6859b2STodor Tomov 718ec6859b2STodor Tomov for (i = nclocks - 1; i >= 0; i--) 719ec6859b2STodor Tomov clk_disable_unprepare(clock[i].clk); 720ec6859b2STodor Tomov } 721ec6859b2STodor Tomov 722ec6859b2STodor Tomov /* 723ec6859b2STodor Tomov * camss_find_sensor - Find a linked media entity which represents a sensor 724ec6859b2STodor Tomov * @entity: Media entity to start searching from 725ec6859b2STodor Tomov * 726ec6859b2STodor Tomov * Return a pointer to sensor media entity or NULL if not found 727ec6859b2STodor Tomov */ 72825f5c34bSTodor Tomov struct media_entity *camss_find_sensor(struct media_entity *entity) 729ec6859b2STodor Tomov { 730ec6859b2STodor Tomov struct media_pad *pad; 731ec6859b2STodor Tomov 732ec6859b2STodor Tomov while (1) { 733ec6859b2STodor Tomov pad = &entity->pads[0]; 734ec6859b2STodor Tomov if (!(pad->flags & MEDIA_PAD_FL_SINK)) 735ec6859b2STodor Tomov return NULL; 736ec6859b2STodor Tomov 737ec6859b2STodor Tomov pad = media_entity_remote_pad(pad); 738ec6859b2STodor Tomov if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 739ec6859b2STodor Tomov return NULL; 740ec6859b2STodor Tomov 741ec6859b2STodor Tomov entity = pad->entity; 742ec6859b2STodor Tomov 743ec6859b2STodor Tomov if (entity->function == MEDIA_ENT_F_CAM_SENSOR) 744ec6859b2STodor Tomov return entity; 745ec6859b2STodor Tomov } 746ec6859b2STodor Tomov } 747ec6859b2STodor Tomov 74878c2cc28SAndrey Konovalov /** 74978c2cc28SAndrey Konovalov * camss_get_link_freq - Get link frequency from sensor 75078c2cc28SAndrey Konovalov * @entity: Media entity in the current pipeline 75178c2cc28SAndrey Konovalov * @bpp: Number of bits per pixel for the current format 75278c2cc28SAndrey Konovalov * @lanes: Number of lanes in the link to the sensor 75378c2cc28SAndrey Konovalov * 75478c2cc28SAndrey Konovalov * Return link frequency on success or a negative error code otherwise 75578c2cc28SAndrey Konovalov */ 75678c2cc28SAndrey Konovalov s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp, 75778c2cc28SAndrey Konovalov unsigned int lanes) 75878c2cc28SAndrey Konovalov { 75978c2cc28SAndrey Konovalov struct media_entity *sensor; 76078c2cc28SAndrey Konovalov struct v4l2_subdev *subdev; 76178c2cc28SAndrey Konovalov 76278c2cc28SAndrey Konovalov sensor = camss_find_sensor(entity); 76378c2cc28SAndrey Konovalov if (!sensor) 76478c2cc28SAndrey Konovalov return -ENODEV; 76578c2cc28SAndrey Konovalov 76678c2cc28SAndrey Konovalov subdev = media_entity_to_v4l2_subdev(sensor); 76778c2cc28SAndrey Konovalov 76878c2cc28SAndrey Konovalov return v4l2_get_link_freq(subdev->ctrl_handler, bpp, 2 * lanes); 76978c2cc28SAndrey Konovalov } 77078c2cc28SAndrey Konovalov 771ec6859b2STodor Tomov /* 772ec6859b2STodor Tomov * camss_get_pixel_clock - Get pixel clock rate from sensor 773ec6859b2STodor Tomov * @entity: Media entity in the current pipeline 774ec6859b2STodor Tomov * @pixel_clock: Received pixel clock value 775ec6859b2STodor Tomov * 776ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 777ec6859b2STodor Tomov */ 7782f908577SVladimir Lypak int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock) 779ec6859b2STodor Tomov { 780ec6859b2STodor Tomov struct media_entity *sensor; 781ec6859b2STodor Tomov struct v4l2_subdev *subdev; 782ec6859b2STodor Tomov struct v4l2_ctrl *ctrl; 783ec6859b2STodor Tomov 784ec6859b2STodor Tomov sensor = camss_find_sensor(entity); 785ec6859b2STodor Tomov if (!sensor) 786ec6859b2STodor Tomov return -ENODEV; 787ec6859b2STodor Tomov 788ec6859b2STodor Tomov subdev = media_entity_to_v4l2_subdev(sensor); 789ec6859b2STodor Tomov 790ec6859b2STodor Tomov ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE); 791ec6859b2STodor Tomov 792ec6859b2STodor Tomov if (!ctrl) 793ec6859b2STodor Tomov return -EINVAL; 794ec6859b2STodor Tomov 795ec6859b2STodor Tomov *pixel_clock = v4l2_ctrl_g_ctrl_int64(ctrl); 796ec6859b2STodor Tomov 797ec6859b2STodor Tomov return 0; 798ec6859b2STodor Tomov } 799ec6859b2STodor Tomov 80002afa816STodor Tomov int camss_pm_domain_on(struct camss *camss, int id) 80102afa816STodor Tomov { 8022f6f8af6SRobert Foss int ret = 0; 80302afa816STodor Tomov 8042f6f8af6SRobert Foss if (id < camss->vfe_num) { 8052f6f8af6SRobert Foss struct vfe_device *vfe = &camss->vfe[id]; 8062f6f8af6SRobert Foss 8072f6f8af6SRobert Foss ret = vfe->ops->pm_domain_on(vfe); 80802afa816STodor Tomov } 80902afa816STodor Tomov 8102f6f8af6SRobert Foss return ret; 81102afa816STodor Tomov } 81202afa816STodor Tomov 81302afa816STodor Tomov void camss_pm_domain_off(struct camss *camss, int id) 81402afa816STodor Tomov { 8152f6f8af6SRobert Foss if (id < camss->vfe_num) { 8162f6f8af6SRobert Foss struct vfe_device *vfe = &camss->vfe[id]; 8172f6f8af6SRobert Foss 8182f6f8af6SRobert Foss vfe->ops->pm_domain_off(vfe); 8192f6f8af6SRobert Foss } 82002afa816STodor Tomov } 82102afa816STodor Tomov 822ec6859b2STodor Tomov /* 823ec6859b2STodor Tomov * camss_of_parse_endpoint_node - Parse port endpoint node 824ec6859b2STodor Tomov * @dev: Device 825ec6859b2STodor Tomov * @node: Device node to be parsed 826ec6859b2STodor Tomov * @csd: Parsed data from port endpoint node 827ec6859b2STodor Tomov * 828ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 829ec6859b2STodor Tomov */ 830ec6859b2STodor Tomov static int camss_of_parse_endpoint_node(struct device *dev, 831ec6859b2STodor Tomov struct device_node *node, 832ec6859b2STodor Tomov struct camss_async_subdev *csd) 833ec6859b2STodor Tomov { 834ec6859b2STodor Tomov struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg; 835ec6859b2STodor Tomov struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2; 836ec6859b2STodor Tomov struct v4l2_fwnode_endpoint vep = { { 0 } }; 837ec6859b2STodor Tomov unsigned int i; 838ec6859b2STodor Tomov 839ec6859b2STodor Tomov v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); 840ec6859b2STodor Tomov 841ec6859b2STodor Tomov csd->interface.csiphy_id = vep.base.port; 842ec6859b2STodor Tomov 843ec6859b2STodor Tomov mipi_csi2 = &vep.bus.mipi_csi2; 844ec6859b2STodor Tomov lncfg->clk.pos = mipi_csi2->clock_lane; 845ec6859b2STodor Tomov lncfg->clk.pol = mipi_csi2->lane_polarities[0]; 846ec6859b2STodor Tomov lncfg->num_data = mipi_csi2->num_data_lanes; 847ec6859b2STodor Tomov 848ec6859b2STodor Tomov lncfg->data = devm_kcalloc(dev, 849ec6859b2STodor Tomov lncfg->num_data, sizeof(*lncfg->data), 850ec6859b2STodor Tomov GFP_KERNEL); 851ec6859b2STodor Tomov if (!lncfg->data) 852ec6859b2STodor Tomov return -ENOMEM; 853ec6859b2STodor Tomov 854ec6859b2STodor Tomov for (i = 0; i < lncfg->num_data; i++) { 855ec6859b2STodor Tomov lncfg->data[i].pos = mipi_csi2->data_lanes[i]; 856ec6859b2STodor Tomov lncfg->data[i].pol = mipi_csi2->lane_polarities[i + 1]; 857ec6859b2STodor Tomov } 858ec6859b2STodor Tomov 859ec6859b2STodor Tomov return 0; 860ec6859b2STodor Tomov } 861ec6859b2STodor Tomov 862ec6859b2STodor Tomov /* 863ec6859b2STodor Tomov * camss_of_parse_ports - Parse ports node 864ec6859b2STodor Tomov * @dev: Device 865ec6859b2STodor Tomov * @notifier: v4l2_device notifier data 866ec6859b2STodor Tomov * 867ec6859b2STodor Tomov * Return number of "port" nodes found in "ports" node 868ec6859b2STodor Tomov */ 869d079f94cSSteve Longerbeam static int camss_of_parse_ports(struct camss *camss) 870ec6859b2STodor Tomov { 871d079f94cSSteve Longerbeam struct device *dev = camss->dev; 872ec6859b2STodor Tomov struct device_node *node = NULL; 873ec6859b2STodor Tomov struct device_node *remote = NULL; 874d079f94cSSteve Longerbeam int ret, num_subdevs = 0; 875ec6859b2STodor Tomov 876d079f94cSSteve Longerbeam for_each_endpoint_of_node(dev->of_node, node) { 877ec6859b2STodor Tomov struct camss_async_subdev *csd; 878ec6859b2STodor Tomov 879ec6859b2STodor Tomov if (!of_device_is_available(node)) 880ec6859b2STodor Tomov continue; 881ec6859b2STodor Tomov 882ec6859b2STodor Tomov remote = of_graph_get_remote_port_parent(node); 883ec6859b2STodor Tomov if (!remote) { 884ec6859b2STodor Tomov dev_err(dev, "Cannot get remote parent\n"); 885d079f94cSSteve Longerbeam ret = -EINVAL; 886d079f94cSSteve Longerbeam goto err_cleanup; 887ec6859b2STodor Tomov } 888ec6859b2STodor Tomov 889*3c8c1539SSakari Ailus csd = v4l2_async_nf_add_fwnode(&camss->notifier, 890*3c8c1539SSakari Ailus of_fwnode_handle(remote), 891b01edcbdSLaurent Pinchart struct camss_async_subdev); 892016413d9SSakari Ailus of_node_put(remote); 893b01edcbdSLaurent Pinchart if (IS_ERR(csd)) { 894b01edcbdSLaurent Pinchart ret = PTR_ERR(csd); 895d079f94cSSteve Longerbeam goto err_cleanup; 896ec6859b2STodor Tomov } 897ec6859b2STodor Tomov 898d079f94cSSteve Longerbeam ret = camss_of_parse_endpoint_node(dev, node, csd); 899d079f94cSSteve Longerbeam if (ret < 0) 900d079f94cSSteve Longerbeam goto err_cleanup; 901d079f94cSSteve Longerbeam 902d079f94cSSteve Longerbeam num_subdevs++; 903d079f94cSSteve Longerbeam } 904d079f94cSSteve Longerbeam 905d079f94cSSteve Longerbeam return num_subdevs; 906d079f94cSSteve Longerbeam 907d079f94cSSteve Longerbeam err_cleanup: 908d079f94cSSteve Longerbeam of_node_put(node); 909d079f94cSSteve Longerbeam return ret; 910ec6859b2STodor Tomov } 911ec6859b2STodor Tomov 912ec6859b2STodor Tomov /* 913ec6859b2STodor Tomov * camss_init_subdevices - Initialize subdev structures and resources 914ec6859b2STodor Tomov * @camss: CAMSS device 915ec6859b2STodor Tomov * 916ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 917ec6859b2STodor Tomov */ 918ec6859b2STodor Tomov static int camss_init_subdevices(struct camss *camss) 919ec6859b2STodor Tomov { 9209c3e59deSTodor Tomov const struct resources *csiphy_res; 9219c3e59deSTodor Tomov const struct resources *csid_res; 9229c3e59deSTodor Tomov const struct resources_ispif *ispif_res; 9239c3e59deSTodor Tomov const struct resources *vfe_res; 924ec6859b2STodor Tomov unsigned int i; 925ec6859b2STodor Tomov int ret; 926ec6859b2STodor Tomov 9279c3e59deSTodor Tomov if (camss->version == CAMSS_8x16) { 9289c3e59deSTodor Tomov csiphy_res = csiphy_res_8x16; 9299c3e59deSTodor Tomov csid_res = csid_res_8x16; 9309c3e59deSTodor Tomov ispif_res = &ispif_res_8x16; 9319c3e59deSTodor Tomov vfe_res = vfe_res_8x16; 9329c3e59deSTodor Tomov } else if (camss->version == CAMSS_8x96) { 9339c3e59deSTodor Tomov csiphy_res = csiphy_res_8x96; 9349c3e59deSTodor Tomov csid_res = csid_res_8x96; 9359c3e59deSTodor Tomov ispif_res = &ispif_res_8x96; 9369c3e59deSTodor Tomov vfe_res = vfe_res_8x96; 9379e5d1581SAngeloGioacchino Del Regno } else if (camss->version == CAMSS_660) { 9389e5d1581SAngeloGioacchino Del Regno csiphy_res = csiphy_res_660; 9399e5d1581SAngeloGioacchino Del Regno csid_res = csid_res_660; 9409e5d1581SAngeloGioacchino Del Regno ispif_res = &ispif_res_660; 9419e5d1581SAngeloGioacchino Del Regno vfe_res = vfe_res_660; 94270524567SRobert Foss } else if (camss->version == CAMSS_845) { 94370524567SRobert Foss csiphy_res = csiphy_res_845; 94470524567SRobert Foss csid_res = csid_res_845; 94570524567SRobert Foss /* Titan VFEs don't have an ISPIF */ 94670524567SRobert Foss ispif_res = NULL; 94770524567SRobert Foss vfe_res = vfe_res_845; 9489c3e59deSTodor Tomov } else { 9499c3e59deSTodor Tomov return -EINVAL; 9509c3e59deSTodor Tomov } 9519c3e59deSTodor Tomov 9529c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) { 9539c3e59deSTodor Tomov ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i], 954ec6859b2STodor Tomov &csiphy_res[i], i); 955ec6859b2STodor Tomov if (ret < 0) { 956ec6859b2STodor Tomov dev_err(camss->dev, 957ec6859b2STodor Tomov "Failed to init csiphy%d sub-device: %d\n", 958ec6859b2STodor Tomov i, ret); 959ec6859b2STodor Tomov return ret; 960ec6859b2STodor Tomov } 961ec6859b2STodor Tomov } 962ec6859b2STodor Tomov 9639c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) { 9649c3e59deSTodor Tomov ret = msm_csid_subdev_init(camss, &camss->csid[i], 965ec6859b2STodor Tomov &csid_res[i], i); 966ec6859b2STodor Tomov if (ret < 0) { 967ec6859b2STodor Tomov dev_err(camss->dev, 968ec6859b2STodor Tomov "Failed to init csid%d sub-device: %d\n", 969ec6859b2STodor Tomov i, ret); 970ec6859b2STodor Tomov return ret; 971ec6859b2STodor Tomov } 972ec6859b2STodor Tomov } 973ec6859b2STodor Tomov 9749d95baf9SRobert Foss ret = msm_ispif_subdev_init(camss, ispif_res); 975ec6859b2STodor Tomov if (ret < 0) { 976ec6859b2STodor Tomov dev_err(camss->dev, "Failed to init ispif sub-device: %d\n", 977ec6859b2STodor Tomov ret); 978ec6859b2STodor Tomov return ret; 979ec6859b2STodor Tomov } 980ec6859b2STodor Tomov 9819c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) { 9829c3e59deSTodor Tomov ret = msm_vfe_subdev_init(camss, &camss->vfe[i], 9839c3e59deSTodor Tomov &vfe_res[i], i); 984ec6859b2STodor Tomov if (ret < 0) { 9859c3e59deSTodor Tomov dev_err(camss->dev, 9869c3e59deSTodor Tomov "Fail to init vfe%d sub-device: %d\n", i, ret); 987ec6859b2STodor Tomov return ret; 988ec6859b2STodor Tomov } 9899c3e59deSTodor Tomov } 990ec6859b2STodor Tomov 991ec6859b2STodor Tomov return 0; 992ec6859b2STodor Tomov } 993ec6859b2STodor Tomov 994ec6859b2STodor Tomov /* 995ec6859b2STodor Tomov * camss_register_entities - Register subdev nodes and create links 996ec6859b2STodor Tomov * @camss: CAMSS device 997ec6859b2STodor Tomov * 998ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 999ec6859b2STodor Tomov */ 1000ec6859b2STodor Tomov static int camss_register_entities(struct camss *camss) 1001ec6859b2STodor Tomov { 10029c3e59deSTodor Tomov int i, j, k; 1003ec6859b2STodor Tomov int ret; 1004ec6859b2STodor Tomov 10059c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) { 1006ec6859b2STodor Tomov ret = msm_csiphy_register_entity(&camss->csiphy[i], 1007ec6859b2STodor Tomov &camss->v4l2_dev); 1008ec6859b2STodor Tomov if (ret < 0) { 1009ec6859b2STodor Tomov dev_err(camss->dev, 1010ec6859b2STodor Tomov "Failed to register csiphy%d entity: %d\n", 1011ec6859b2STodor Tomov i, ret); 1012ec6859b2STodor Tomov goto err_reg_csiphy; 1013ec6859b2STodor Tomov } 1014ec6859b2STodor Tomov } 1015ec6859b2STodor Tomov 10169c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) { 1017ec6859b2STodor Tomov ret = msm_csid_register_entity(&camss->csid[i], 1018ec6859b2STodor Tomov &camss->v4l2_dev); 1019ec6859b2STodor Tomov if (ret < 0) { 1020ec6859b2STodor Tomov dev_err(camss->dev, 1021ec6859b2STodor Tomov "Failed to register csid%d entity: %d\n", 1022ec6859b2STodor Tomov i, ret); 1023ec6859b2STodor Tomov goto err_reg_csid; 1024ec6859b2STodor Tomov } 1025ec6859b2STodor Tomov } 1026ec6859b2STodor Tomov 10279d95baf9SRobert Foss ret = msm_ispif_register_entities(camss->ispif, 10289d95baf9SRobert Foss &camss->v4l2_dev); 1029ec6859b2STodor Tomov if (ret < 0) { 1030ec6859b2STodor Tomov dev_err(camss->dev, "Failed to register ispif entities: %d\n", 1031ec6859b2STodor Tomov ret); 1032ec6859b2STodor Tomov goto err_reg_ispif; 1033ec6859b2STodor Tomov } 1034ec6859b2STodor Tomov 10359c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) { 10369c3e59deSTodor Tomov ret = msm_vfe_register_entities(&camss->vfe[i], 10379c3e59deSTodor Tomov &camss->v4l2_dev); 1038ec6859b2STodor Tomov if (ret < 0) { 10399c3e59deSTodor Tomov dev_err(camss->dev, 10409c3e59deSTodor Tomov "Failed to register vfe%d entities: %d\n", 10419c3e59deSTodor Tomov i, ret); 1042ec6859b2STodor Tomov goto err_reg_vfe; 1043ec6859b2STodor Tomov } 10449c3e59deSTodor Tomov } 1045ec6859b2STodor Tomov 10469c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) { 10479c3e59deSTodor Tomov for (j = 0; j < camss->csid_num; j++) { 1048ec6859b2STodor Tomov ret = media_create_pad_link( 1049ec6859b2STodor Tomov &camss->csiphy[i].subdev.entity, 1050ec6859b2STodor Tomov MSM_CSIPHY_PAD_SRC, 1051ec6859b2STodor Tomov &camss->csid[j].subdev.entity, 1052ec6859b2STodor Tomov MSM_CSID_PAD_SINK, 1053ec6859b2STodor Tomov 0); 1054ec6859b2STodor Tomov if (ret < 0) { 1055ec6859b2STodor Tomov dev_err(camss->dev, 1056ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 1057ec6859b2STodor Tomov camss->csiphy[i].subdev.entity.name, 1058ec6859b2STodor Tomov camss->csid[j].subdev.entity.name, 1059ec6859b2STodor Tomov ret); 1060ec6859b2STodor Tomov goto err_link; 1061ec6859b2STodor Tomov } 1062ec6859b2STodor Tomov } 1063ec6859b2STodor Tomov } 1064ec6859b2STodor Tomov 10659d95baf9SRobert Foss if (camss->ispif) { 10669c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) { 10679d95baf9SRobert Foss for (j = 0; j < camss->ispif->line_num; j++) { 1068ec6859b2STodor Tomov ret = media_create_pad_link( 1069ec6859b2STodor Tomov &camss->csid[i].subdev.entity, 1070ec6859b2STodor Tomov MSM_CSID_PAD_SRC, 10719d95baf9SRobert Foss &camss->ispif->line[j].subdev.entity, 1072ec6859b2STodor Tomov MSM_ISPIF_PAD_SINK, 1073ec6859b2STodor Tomov 0); 1074ec6859b2STodor Tomov if (ret < 0) { 1075ec6859b2STodor Tomov dev_err(camss->dev, 1076ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 1077ec6859b2STodor Tomov camss->csid[i].subdev.entity.name, 10789d95baf9SRobert Foss camss->ispif->line[j].subdev.entity.name, 1079ec6859b2STodor Tomov ret); 1080ec6859b2STodor Tomov goto err_link; 1081ec6859b2STodor Tomov } 1082ec6859b2STodor Tomov } 1083ec6859b2STodor Tomov } 1084ec6859b2STodor Tomov 10859d95baf9SRobert Foss for (i = 0; i < camss->ispif->line_num; i++) 10869c3e59deSTodor Tomov for (k = 0; k < camss->vfe_num; k++) 1087633b388fSRobert Foss for (j = 0; j < camss->vfe[k].line_num; j++) { 10889d95baf9SRobert Foss struct v4l2_subdev *ispif = &camss->ispif->line[i].subdev; 10899d95baf9SRobert Foss struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; 10909d95baf9SRobert Foss 10919d95baf9SRobert Foss ret = media_create_pad_link(&ispif->entity, 1092ec6859b2STodor Tomov MSM_ISPIF_PAD_SRC, 10939d95baf9SRobert Foss &vfe->entity, 1094ec6859b2STodor Tomov MSM_VFE_PAD_SINK, 1095ec6859b2STodor Tomov 0); 1096ec6859b2STodor Tomov if (ret < 0) { 1097ec6859b2STodor Tomov dev_err(camss->dev, 1098ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 10999d95baf9SRobert Foss ispif->entity.name, 11009d95baf9SRobert Foss vfe->entity.name, 1101ec6859b2STodor Tomov ret); 1102ec6859b2STodor Tomov goto err_link; 1103ec6859b2STodor Tomov } 1104ec6859b2STodor Tomov } 11059d95baf9SRobert Foss } else { 11069d95baf9SRobert Foss for (i = 0; i < camss->csid_num; i++) 11079d95baf9SRobert Foss for (k = 0; k < camss->vfe_num; k++) 1108633b388fSRobert Foss for (j = 0; j < camss->vfe[k].line_num; j++) { 11099d95baf9SRobert Foss struct v4l2_subdev *csid = &camss->csid[i].subdev; 11109d95baf9SRobert Foss struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; 11119d95baf9SRobert Foss 11129d95baf9SRobert Foss ret = media_create_pad_link(&csid->entity, 11139d95baf9SRobert Foss MSM_CSID_PAD_SRC, 11149d95baf9SRobert Foss &vfe->entity, 11159d95baf9SRobert Foss MSM_VFE_PAD_SINK, 11169d95baf9SRobert Foss 0); 11179d95baf9SRobert Foss if (ret < 0) { 11189d95baf9SRobert Foss dev_err(camss->dev, 11199d95baf9SRobert Foss "Failed to link %s->%s entities: %d\n", 11209d95baf9SRobert Foss csid->entity.name, 11219d95baf9SRobert Foss vfe->entity.name, 11229d95baf9SRobert Foss ret); 11239d95baf9SRobert Foss goto err_link; 11249d95baf9SRobert Foss } 11259d95baf9SRobert Foss } 11269d95baf9SRobert Foss } 1127ec6859b2STodor Tomov 1128ec6859b2STodor Tomov return 0; 1129ec6859b2STodor Tomov 1130ec6859b2STodor Tomov err_link: 11319c3e59deSTodor Tomov i = camss->vfe_num; 1132ec6859b2STodor Tomov err_reg_vfe: 11339c3e59deSTodor Tomov for (i--; i >= 0; i--) 11349c3e59deSTodor Tomov msm_vfe_unregister_entities(&camss->vfe[i]); 11359c3e59deSTodor Tomov 1136ec6859b2STodor Tomov err_reg_ispif: 11379d95baf9SRobert Foss msm_ispif_unregister_entities(camss->ispif); 1138ec6859b2STodor Tomov 11399c3e59deSTodor Tomov i = camss->csid_num; 1140ec6859b2STodor Tomov err_reg_csid: 1141ec6859b2STodor Tomov for (i--; i >= 0; i--) 1142ec6859b2STodor Tomov msm_csid_unregister_entity(&camss->csid[i]); 1143ec6859b2STodor Tomov 11449c3e59deSTodor Tomov i = camss->csiphy_num; 1145ec6859b2STodor Tomov err_reg_csiphy: 1146ec6859b2STodor Tomov for (i--; i >= 0; i--) 1147ec6859b2STodor Tomov msm_csiphy_unregister_entity(&camss->csiphy[i]); 1148ec6859b2STodor Tomov 1149ec6859b2STodor Tomov return ret; 1150ec6859b2STodor Tomov } 1151ec6859b2STodor Tomov 1152ec6859b2STodor Tomov /* 1153ec6859b2STodor Tomov * camss_unregister_entities - Unregister subdev nodes 1154ec6859b2STodor Tomov * @camss: CAMSS device 1155ec6859b2STodor Tomov * 1156ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 1157ec6859b2STodor Tomov */ 1158ec6859b2STodor Tomov static void camss_unregister_entities(struct camss *camss) 1159ec6859b2STodor Tomov { 1160ec6859b2STodor Tomov unsigned int i; 1161ec6859b2STodor Tomov 11629c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) 1163ec6859b2STodor Tomov msm_csiphy_unregister_entity(&camss->csiphy[i]); 1164ec6859b2STodor Tomov 11659c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) 1166ec6859b2STodor Tomov msm_csid_unregister_entity(&camss->csid[i]); 1167ec6859b2STodor Tomov 11689d95baf9SRobert Foss msm_ispif_unregister_entities(camss->ispif); 11699c3e59deSTodor Tomov 11709c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) 11719c3e59deSTodor Tomov msm_vfe_unregister_entities(&camss->vfe[i]); 1172ec6859b2STodor Tomov } 1173ec6859b2STodor Tomov 1174ec6859b2STodor Tomov static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async, 1175ec6859b2STodor Tomov struct v4l2_subdev *subdev, 1176ec6859b2STodor Tomov struct v4l2_async_subdev *asd) 1177ec6859b2STodor Tomov { 1178ec6859b2STodor Tomov struct camss *camss = container_of(async, struct camss, notifier); 1179ec6859b2STodor Tomov struct camss_async_subdev *csd = 1180ec6859b2STodor Tomov container_of(asd, struct camss_async_subdev, asd); 1181ec6859b2STodor Tomov u8 id = csd->interface.csiphy_id; 1182ec6859b2STodor Tomov struct csiphy_device *csiphy = &camss->csiphy[id]; 1183ec6859b2STodor Tomov 1184ec6859b2STodor Tomov csiphy->cfg.csi2 = &csd->interface.csi2; 1185ec6859b2STodor Tomov subdev->host_priv = csiphy; 1186ec6859b2STodor Tomov 1187ec6859b2STodor Tomov return 0; 1188ec6859b2STodor Tomov } 1189ec6859b2STodor Tomov 1190ec6859b2STodor Tomov static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async) 1191ec6859b2STodor Tomov { 1192ec6859b2STodor Tomov struct camss *camss = container_of(async, struct camss, notifier); 1193ec6859b2STodor Tomov struct v4l2_device *v4l2_dev = &camss->v4l2_dev; 1194ec6859b2STodor Tomov struct v4l2_subdev *sd; 1195ec6859b2STodor Tomov int ret; 1196ec6859b2STodor Tomov 1197ec6859b2STodor Tomov list_for_each_entry(sd, &v4l2_dev->subdevs, list) { 1198ec6859b2STodor Tomov if (sd->host_priv) { 1199ec6859b2STodor Tomov struct media_entity *sensor = &sd->entity; 1200ec6859b2STodor Tomov struct csiphy_device *csiphy = 1201ec6859b2STodor Tomov (struct csiphy_device *) sd->host_priv; 1202ec6859b2STodor Tomov struct media_entity *input = &csiphy->subdev.entity; 1203ec6859b2STodor Tomov unsigned int i; 1204ec6859b2STodor Tomov 1205ec6859b2STodor Tomov for (i = 0; i < sensor->num_pads; i++) { 1206ec6859b2STodor Tomov if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE) 1207ec6859b2STodor Tomov break; 1208ec6859b2STodor Tomov } 1209ec6859b2STodor Tomov if (i == sensor->num_pads) { 1210ec6859b2STodor Tomov dev_err(camss->dev, 1211ec6859b2STodor Tomov "No source pad in external entity\n"); 1212ec6859b2STodor Tomov return -EINVAL; 1213ec6859b2STodor Tomov } 1214ec6859b2STodor Tomov 1215ec6859b2STodor Tomov ret = media_create_pad_link(sensor, i, 1216ec6859b2STodor Tomov input, MSM_CSIPHY_PAD_SINK, 1217ec6859b2STodor Tomov MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); 1218ec6859b2STodor Tomov if (ret < 0) { 1219ec6859b2STodor Tomov dev_err(camss->dev, 1220ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 1221ec6859b2STodor Tomov sensor->name, input->name, ret); 1222ec6859b2STodor Tomov return ret; 1223ec6859b2STodor Tomov } 1224ec6859b2STodor Tomov } 1225ec6859b2STodor Tomov } 1226ec6859b2STodor Tomov 1227ec6859b2STodor Tomov ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); 1228ec6859b2STodor Tomov if (ret < 0) 1229ec6859b2STodor Tomov return ret; 1230ec6859b2STodor Tomov 1231ec6859b2STodor Tomov return media_device_register(&camss->media_dev); 1232ec6859b2STodor Tomov } 1233ec6859b2STodor Tomov 1234ec6859b2STodor Tomov static const struct v4l2_async_notifier_operations camss_subdev_notifier_ops = { 1235ec6859b2STodor Tomov .bound = camss_subdev_notifier_bound, 1236ec6859b2STodor Tomov .complete = camss_subdev_notifier_complete, 1237ec6859b2STodor Tomov }; 1238ec6859b2STodor Tomov 1239ec6859b2STodor Tomov static const struct media_device_ops camss_media_ops = { 1240ec6859b2STodor Tomov .link_notify = v4l2_pipeline_link_notify, 1241ec6859b2STodor Tomov }; 1242ec6859b2STodor Tomov 12432f6f8af6SRobert Foss static int camss_configure_pd(struct camss *camss) 12442f6f8af6SRobert Foss { 12452f6f8af6SRobert Foss int nbr_pm_domains = 0; 12462f6f8af6SRobert Foss int last_pm_domain = 0; 12472f6f8af6SRobert Foss int i; 12482f6f8af6SRobert Foss int ret; 12492f6f8af6SRobert Foss 12502f6f8af6SRobert Foss if (camss->version == CAMSS_8x96 || 12512f6f8af6SRobert Foss camss->version == CAMSS_660) 12522f6f8af6SRobert Foss nbr_pm_domains = PM_DOMAIN_GEN1_COUNT; 125370524567SRobert Foss else if (camss->version == CAMSS_845) 125470524567SRobert Foss nbr_pm_domains = PM_DOMAIN_GEN2_COUNT; 12552f6f8af6SRobert Foss 12562f6f8af6SRobert Foss for (i = 0; i < nbr_pm_domains; i++) { 12572f6f8af6SRobert Foss camss->genpd[i] = dev_pm_domain_attach_by_id(camss->dev, i); 12582f6f8af6SRobert Foss if (IS_ERR(camss->genpd[i])) { 12592f6f8af6SRobert Foss ret = PTR_ERR(camss->genpd[i]); 12602f6f8af6SRobert Foss goto fail_pm; 12612f6f8af6SRobert Foss } 12622f6f8af6SRobert Foss 12632f6f8af6SRobert Foss camss->genpd_link[i] = device_link_add(camss->dev, camss->genpd[i], 12642f6f8af6SRobert Foss DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | 12652f6f8af6SRobert Foss DL_FLAG_RPM_ACTIVE); 12662f6f8af6SRobert Foss if (!camss->genpd_link[i]) { 12672f6f8af6SRobert Foss dev_pm_domain_detach(camss->genpd[i], true); 12682f6f8af6SRobert Foss ret = -EINVAL; 12692f6f8af6SRobert Foss goto fail_pm; 12702f6f8af6SRobert Foss } 12712f6f8af6SRobert Foss 12722f6f8af6SRobert Foss last_pm_domain = i; 12732f6f8af6SRobert Foss } 12742f6f8af6SRobert Foss 12752f6f8af6SRobert Foss return 0; 12762f6f8af6SRobert Foss 12772f6f8af6SRobert Foss fail_pm: 12782f6f8af6SRobert Foss for (i = 0; i < last_pm_domain; i++) { 12792f6f8af6SRobert Foss device_link_del(camss->genpd_link[i]); 12802f6f8af6SRobert Foss dev_pm_domain_detach(camss->genpd[i], true); 12812f6f8af6SRobert Foss } 12822f6f8af6SRobert Foss 12832f6f8af6SRobert Foss return ret; 12842f6f8af6SRobert Foss } 12852f6f8af6SRobert Foss 1286ec6859b2STodor Tomov /* 1287ec6859b2STodor Tomov * camss_probe - Probe CAMSS platform device 1288ec6859b2STodor Tomov * @pdev: Pointer to CAMSS platform device 1289ec6859b2STodor Tomov * 1290ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 1291ec6859b2STodor Tomov */ 1292ec6859b2STodor Tomov static int camss_probe(struct platform_device *pdev) 1293ec6859b2STodor Tomov { 1294ec6859b2STodor Tomov struct device *dev = &pdev->dev; 1295ec6859b2STodor Tomov struct camss *camss; 1296d079f94cSSteve Longerbeam int num_subdevs, ret; 1297ec6859b2STodor Tomov 1298ec6859b2STodor Tomov camss = kzalloc(sizeof(*camss), GFP_KERNEL); 1299ec6859b2STodor Tomov if (!camss) 1300ec6859b2STodor Tomov return -ENOMEM; 1301ec6859b2STodor Tomov 1302ec6859b2STodor Tomov atomic_set(&camss->ref_count, 0); 1303ec6859b2STodor Tomov camss->dev = dev; 1304ec6859b2STodor Tomov platform_set_drvdata(pdev, camss); 1305ec6859b2STodor Tomov 13069c3e59deSTodor Tomov if (of_device_is_compatible(dev->of_node, "qcom,msm8916-camss")) { 13079c3e59deSTodor Tomov camss->version = CAMSS_8x16; 13089c3e59deSTodor Tomov camss->csiphy_num = 2; 13099c3e59deSTodor Tomov camss->csid_num = 2; 13109c3e59deSTodor Tomov camss->vfe_num = 1; 13119c3e59deSTodor Tomov } else if (of_device_is_compatible(dev->of_node, 13129c3e59deSTodor Tomov "qcom,msm8996-camss")) { 13139c3e59deSTodor Tomov camss->version = CAMSS_8x96; 13149c3e59deSTodor Tomov camss->csiphy_num = 3; 13159c3e59deSTodor Tomov camss->csid_num = 4; 13169c3e59deSTodor Tomov camss->vfe_num = 2; 13179e5d1581SAngeloGioacchino Del Regno } else if (of_device_is_compatible(dev->of_node, 13189e5d1581SAngeloGioacchino Del Regno "qcom,sdm660-camss")) { 13199e5d1581SAngeloGioacchino Del Regno camss->version = CAMSS_660; 13209e5d1581SAngeloGioacchino Del Regno camss->csiphy_num = 3; 13219e5d1581SAngeloGioacchino Del Regno camss->csid_num = 4; 13229e5d1581SAngeloGioacchino Del Regno camss->vfe_num = 2; 132370524567SRobert Foss } else if (of_device_is_compatible(dev->of_node, 132470524567SRobert Foss "qcom,sdm845-camss")) { 132570524567SRobert Foss camss->version = CAMSS_845; 132670524567SRobert Foss camss->csiphy_num = 4; 132770524567SRobert Foss camss->csid_num = 3; 132870524567SRobert Foss camss->vfe_num = 3; 13299c3e59deSTodor Tomov } else { 1330f45882cfSEvgeny Novikov ret = -EINVAL; 1331f45882cfSEvgeny Novikov goto err_free; 13329c3e59deSTodor Tomov } 13339c3e59deSTodor Tomov 133455b51899STodor Tomov camss->csiphy = devm_kcalloc(dev, camss->csiphy_num, 133555b51899STodor Tomov sizeof(*camss->csiphy), GFP_KERNEL); 1336f45882cfSEvgeny Novikov if (!camss->csiphy) { 1337f45882cfSEvgeny Novikov ret = -ENOMEM; 1338f45882cfSEvgeny Novikov goto err_free; 1339f45882cfSEvgeny Novikov } 13409c3e59deSTodor Tomov 134155b51899STodor Tomov camss->csid = devm_kcalloc(dev, camss->csid_num, sizeof(*camss->csid), 13429c3e59deSTodor Tomov GFP_KERNEL); 1343f45882cfSEvgeny Novikov if (!camss->csid) { 1344f45882cfSEvgeny Novikov ret = -ENOMEM; 1345f45882cfSEvgeny Novikov goto err_free; 1346f45882cfSEvgeny Novikov } 13479c3e59deSTodor Tomov 13489d95baf9SRobert Foss if (camss->version == CAMSS_8x16 || 13499d95baf9SRobert Foss camss->version == CAMSS_8x96) { 13509d95baf9SRobert Foss camss->ispif = devm_kcalloc(dev, 1, sizeof(*camss->ispif), GFP_KERNEL); 13519d95baf9SRobert Foss if (!camss->ispif) { 13529d95baf9SRobert Foss ret = -ENOMEM; 13539d95baf9SRobert Foss goto err_free; 13549d95baf9SRobert Foss } 13559d95baf9SRobert Foss } 13569d95baf9SRobert Foss 135755b51899STodor Tomov camss->vfe = devm_kcalloc(dev, camss->vfe_num, sizeof(*camss->vfe), 135855b51899STodor Tomov GFP_KERNEL); 1359f45882cfSEvgeny Novikov if (!camss->vfe) { 1360f45882cfSEvgeny Novikov ret = -ENOMEM; 1361f45882cfSEvgeny Novikov goto err_free; 1362f45882cfSEvgeny Novikov } 13639c3e59deSTodor Tomov 1364*3c8c1539SSakari Ailus v4l2_async_nf_init(&camss->notifier); 1365d079f94cSSteve Longerbeam 1366d079f94cSSteve Longerbeam num_subdevs = camss_of_parse_ports(camss); 1367f45882cfSEvgeny Novikov if (num_subdevs < 0) { 1368f45882cfSEvgeny Novikov ret = num_subdevs; 1369f45882cfSEvgeny Novikov goto err_cleanup; 1370f45882cfSEvgeny Novikov } 1371ec6859b2STodor Tomov 1372ec6859b2STodor Tomov ret = camss_init_subdevices(camss); 1373ec6859b2STodor Tomov if (ret < 0) 1374d079f94cSSteve Longerbeam goto err_cleanup; 1375ec6859b2STodor Tomov 1376ec6859b2STodor Tomov ret = dma_set_mask_and_coherent(dev, 0xffffffff); 1377ec6859b2STodor Tomov if (ret) 1378d079f94cSSteve Longerbeam goto err_cleanup; 1379ec6859b2STodor Tomov 1380ec6859b2STodor Tomov camss->media_dev.dev = camss->dev; 1381c0decac1SMauro Carvalho Chehab strscpy(camss->media_dev.model, "Qualcomm Camera Subsystem", 1382ec6859b2STodor Tomov sizeof(camss->media_dev.model)); 1383ec6859b2STodor Tomov camss->media_dev.ops = &camss_media_ops; 1384ec6859b2STodor Tomov media_device_init(&camss->media_dev); 1385ec6859b2STodor Tomov 1386ec6859b2STodor Tomov camss->v4l2_dev.mdev = &camss->media_dev; 1387ec6859b2STodor Tomov ret = v4l2_device_register(camss->dev, &camss->v4l2_dev); 1388ec6859b2STodor Tomov if (ret < 0) { 1389ec6859b2STodor Tomov dev_err(dev, "Failed to register V4L2 device: %d\n", ret); 1390d079f94cSSteve Longerbeam goto err_cleanup; 1391ec6859b2STodor Tomov } 1392ec6859b2STodor Tomov 1393ec6859b2STodor Tomov ret = camss_register_entities(camss); 1394ec6859b2STodor Tomov if (ret < 0) 1395ec6859b2STodor Tomov goto err_register_entities; 1396ec6859b2STodor Tomov 1397d079f94cSSteve Longerbeam if (num_subdevs) { 1398ec6859b2STodor Tomov camss->notifier.ops = &camss_subdev_notifier_ops; 1399ec6859b2STodor Tomov 1400*3c8c1539SSakari Ailus ret = v4l2_async_nf_register(&camss->v4l2_dev, 1401ec6859b2STodor Tomov &camss->notifier); 1402ec6859b2STodor Tomov if (ret) { 1403ec6859b2STodor Tomov dev_err(dev, 1404ec6859b2STodor Tomov "Failed to register async subdev nodes: %d\n", 1405ec6859b2STodor Tomov ret); 1406ec6859b2STodor Tomov goto err_register_subdevs; 1407ec6859b2STodor Tomov } 1408ec6859b2STodor Tomov } else { 1409ec6859b2STodor Tomov ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); 1410ec6859b2STodor Tomov if (ret < 0) { 1411ec6859b2STodor Tomov dev_err(dev, "Failed to register subdev nodes: %d\n", 1412ec6859b2STodor Tomov ret); 1413ec6859b2STodor Tomov goto err_register_subdevs; 1414ec6859b2STodor Tomov } 1415ec6859b2STodor Tomov 1416ec6859b2STodor Tomov ret = media_device_register(&camss->media_dev); 1417ec6859b2STodor Tomov if (ret < 0) { 1418ec6859b2STodor Tomov dev_err(dev, "Failed to register media device: %d\n", 1419ec6859b2STodor Tomov ret); 1420ec6859b2STodor Tomov goto err_register_subdevs; 1421ec6859b2STodor Tomov } 1422ec6859b2STodor Tomov } 1423ec6859b2STodor Tomov 14242f6f8af6SRobert Foss ret = camss_configure_pd(camss); 14252f6f8af6SRobert Foss if (ret < 0) { 14262f6f8af6SRobert Foss dev_err(dev, "Failed to configure power domains: %d\n", ret); 14272f6f8af6SRobert Foss return ret; 142802afa816STodor Tomov } 142902afa816STodor Tomov 143002afa816STodor Tomov pm_runtime_enable(dev); 143102afa816STodor Tomov 1432ec6859b2STodor Tomov return 0; 1433ec6859b2STodor Tomov 1434ec6859b2STodor Tomov err_register_subdevs: 1435ec6859b2STodor Tomov camss_unregister_entities(camss); 1436ec6859b2STodor Tomov err_register_entities: 1437ec6859b2STodor Tomov v4l2_device_unregister(&camss->v4l2_dev); 1438d079f94cSSteve Longerbeam err_cleanup: 1439*3c8c1539SSakari Ailus v4l2_async_nf_cleanup(&camss->notifier); 1440f45882cfSEvgeny Novikov err_free: 1441f45882cfSEvgeny Novikov kfree(camss); 1442ec6859b2STodor Tomov 1443ec6859b2STodor Tomov return ret; 1444ec6859b2STodor Tomov } 1445ec6859b2STodor Tomov 1446ec6859b2STodor Tomov void camss_delete(struct camss *camss) 1447ec6859b2STodor Tomov { 14482f6f8af6SRobert Foss int nbr_pm_domains = 0; 14492f6f8af6SRobert Foss int i; 14502f6f8af6SRobert Foss 1451ec6859b2STodor Tomov v4l2_device_unregister(&camss->v4l2_dev); 1452ec6859b2STodor Tomov media_device_unregister(&camss->media_dev); 1453ec6859b2STodor Tomov media_device_cleanup(&camss->media_dev); 1454ec6859b2STodor Tomov 145502afa816STodor Tomov pm_runtime_disable(camss->dev); 145602afa816STodor Tomov 14579e5d1581SAngeloGioacchino Del Regno if (camss->version == CAMSS_8x96 || 14582f6f8af6SRobert Foss camss->version == CAMSS_660) 14592f6f8af6SRobert Foss nbr_pm_domains = PM_DOMAIN_GEN1_COUNT; 146070524567SRobert Foss else if (camss->version == CAMSS_845) 146170524567SRobert Foss nbr_pm_domains = PM_DOMAIN_GEN2_COUNT; 14622f6f8af6SRobert Foss 14632f6f8af6SRobert Foss for (i = 0; i < nbr_pm_domains; i++) { 14642f6f8af6SRobert Foss device_link_del(camss->genpd_link[i]); 14652f6f8af6SRobert Foss dev_pm_domain_detach(camss->genpd[i], true); 146602afa816STodor Tomov } 146702afa816STodor Tomov 1468ec6859b2STodor Tomov kfree(camss); 1469ec6859b2STodor Tomov } 1470ec6859b2STodor Tomov 1471ec6859b2STodor Tomov /* 1472ec6859b2STodor Tomov * camss_remove - Remove CAMSS platform device 1473ec6859b2STodor Tomov * @pdev: Pointer to CAMSS platform device 1474ec6859b2STodor Tomov * 1475ec6859b2STodor Tomov * Always returns 0. 1476ec6859b2STodor Tomov */ 1477ec6859b2STodor Tomov static int camss_remove(struct platform_device *pdev) 1478ec6859b2STodor Tomov { 1479ec6859b2STodor Tomov struct camss *camss = platform_get_drvdata(pdev); 1480ec6859b2STodor Tomov 1481*3c8c1539SSakari Ailus v4l2_async_nf_unregister(&camss->notifier); 1482*3c8c1539SSakari Ailus v4l2_async_nf_cleanup(&camss->notifier); 1483ec6859b2STodor Tomov camss_unregister_entities(camss); 1484ec6859b2STodor Tomov 1485ec6859b2STodor Tomov if (atomic_read(&camss->ref_count) == 0) 1486ec6859b2STodor Tomov camss_delete(camss); 1487ec6859b2STodor Tomov 1488ec6859b2STodor Tomov return 0; 1489ec6859b2STodor Tomov } 1490ec6859b2STodor Tomov 1491ec6859b2STodor Tomov static const struct of_device_id camss_dt_match[] = { 1492ec6859b2STodor Tomov { .compatible = "qcom,msm8916-camss" }, 14939c3e59deSTodor Tomov { .compatible = "qcom,msm8996-camss" }, 14949e5d1581SAngeloGioacchino Del Regno { .compatible = "qcom,sdm660-camss" }, 149570524567SRobert Foss { .compatible = "qcom,sdm845-camss" }, 1496ec6859b2STodor Tomov { } 1497ec6859b2STodor Tomov }; 1498ec6859b2STodor Tomov 1499ec6859b2STodor Tomov MODULE_DEVICE_TABLE(of, camss_dt_match); 1500ec6859b2STodor Tomov 150144a9ffd4SArnd Bergmann static int __maybe_unused camss_runtime_suspend(struct device *dev) 150202afa816STodor Tomov { 150302afa816STodor Tomov return 0; 150402afa816STodor Tomov } 150502afa816STodor Tomov 150644a9ffd4SArnd Bergmann static int __maybe_unused camss_runtime_resume(struct device *dev) 150702afa816STodor Tomov { 150802afa816STodor Tomov return 0; 150902afa816STodor Tomov } 151002afa816STodor Tomov 151102afa816STodor Tomov static const struct dev_pm_ops camss_pm_ops = { 151202afa816STodor Tomov SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 151302afa816STodor Tomov pm_runtime_force_resume) 151402afa816STodor Tomov SET_RUNTIME_PM_OPS(camss_runtime_suspend, camss_runtime_resume, NULL) 151502afa816STodor Tomov }; 151602afa816STodor Tomov 1517ec6859b2STodor Tomov static struct platform_driver qcom_camss_driver = { 1518ec6859b2STodor Tomov .probe = camss_probe, 1519ec6859b2STodor Tomov .remove = camss_remove, 1520ec6859b2STodor Tomov .driver = { 1521ec6859b2STodor Tomov .name = "qcom-camss", 1522ec6859b2STodor Tomov .of_match_table = camss_dt_match, 152302afa816STodor Tomov .pm = &camss_pm_ops, 1524ec6859b2STodor Tomov }, 1525ec6859b2STodor Tomov }; 1526ec6859b2STodor Tomov 1527ec6859b2STodor Tomov module_platform_driver(qcom_camss_driver); 1528ec6859b2STodor Tomov 1529ec6859b2STodor Tomov MODULE_ALIAS("platform:qcom-camss"); 1530ec6859b2STodor Tomov MODULE_DESCRIPTION("Qualcomm Camera Subsystem driver"); 1531ec6859b2STodor Tomov MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>"); 1532ec6859b2STodor Tomov MODULE_LICENSE("GPL v2"); 1533