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 468ec6859b2STodor Tomov /* 469ec6859b2STodor Tomov * camss_add_clock_margin - Add margin to clock frequency rate 470ec6859b2STodor Tomov * @rate: Clock frequency rate 471ec6859b2STodor Tomov * 472ec6859b2STodor Tomov * When making calculations with physical clock frequency values 473ec6859b2STodor Tomov * some safety margin must be added. Add it. 474ec6859b2STodor Tomov */ 475ec6859b2STodor Tomov inline void camss_add_clock_margin(u64 *rate) 476ec6859b2STodor Tomov { 477ec6859b2STodor Tomov *rate *= CAMSS_CLOCK_MARGIN_NUMERATOR; 478ec6859b2STodor Tomov *rate = div_u64(*rate, CAMSS_CLOCK_MARGIN_DENOMINATOR); 479ec6859b2STodor Tomov } 480ec6859b2STodor Tomov 481ec6859b2STodor Tomov /* 482ec6859b2STodor Tomov * camss_enable_clocks - Enable multiple clocks 483ec6859b2STodor Tomov * @nclocks: Number of clocks in clock array 484ec6859b2STodor Tomov * @clock: Clock array 485ec6859b2STodor Tomov * @dev: Device 486ec6859b2STodor Tomov * 487ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 488ec6859b2STodor Tomov */ 489ec6859b2STodor Tomov int camss_enable_clocks(int nclocks, struct camss_clock *clock, 490ec6859b2STodor Tomov struct device *dev) 491ec6859b2STodor Tomov { 492ec6859b2STodor Tomov int ret; 493ec6859b2STodor Tomov int i; 494ec6859b2STodor Tomov 495ec6859b2STodor Tomov for (i = 0; i < nclocks; i++) { 496ec6859b2STodor Tomov ret = clk_prepare_enable(clock[i].clk); 497ec6859b2STodor Tomov if (ret) { 498ec6859b2STodor Tomov dev_err(dev, "clock enable failed: %d\n", ret); 499ec6859b2STodor Tomov goto error; 500ec6859b2STodor Tomov } 501ec6859b2STodor Tomov } 502ec6859b2STodor Tomov 503ec6859b2STodor Tomov return 0; 504ec6859b2STodor Tomov 505ec6859b2STodor Tomov error: 506ec6859b2STodor Tomov for (i--; i >= 0; i--) 507ec6859b2STodor Tomov clk_disable_unprepare(clock[i].clk); 508ec6859b2STodor Tomov 509ec6859b2STodor Tomov return ret; 510ec6859b2STodor Tomov } 511ec6859b2STodor Tomov 512ec6859b2STodor Tomov /* 513ec6859b2STodor Tomov * camss_disable_clocks - Disable multiple clocks 514ec6859b2STodor Tomov * @nclocks: Number of clocks in clock array 515ec6859b2STodor Tomov * @clock: Clock array 516ec6859b2STodor Tomov */ 517ec6859b2STodor Tomov void camss_disable_clocks(int nclocks, struct camss_clock *clock) 518ec6859b2STodor Tomov { 519ec6859b2STodor Tomov int i; 520ec6859b2STodor Tomov 521ec6859b2STodor Tomov for (i = nclocks - 1; i >= 0; i--) 522ec6859b2STodor Tomov clk_disable_unprepare(clock[i].clk); 523ec6859b2STodor Tomov } 524ec6859b2STodor Tomov 525ec6859b2STodor Tomov /* 526ec6859b2STodor Tomov * camss_find_sensor - Find a linked media entity which represents a sensor 527ec6859b2STodor Tomov * @entity: Media entity to start searching from 528ec6859b2STodor Tomov * 529ec6859b2STodor Tomov * Return a pointer to sensor media entity or NULL if not found 530ec6859b2STodor Tomov */ 53125f5c34bSTodor Tomov struct media_entity *camss_find_sensor(struct media_entity *entity) 532ec6859b2STodor Tomov { 533ec6859b2STodor Tomov struct media_pad *pad; 534ec6859b2STodor Tomov 535ec6859b2STodor Tomov while (1) { 536ec6859b2STodor Tomov pad = &entity->pads[0]; 537ec6859b2STodor Tomov if (!(pad->flags & MEDIA_PAD_FL_SINK)) 538ec6859b2STodor Tomov return NULL; 539ec6859b2STodor Tomov 540ec6859b2STodor Tomov pad = media_entity_remote_pad(pad); 541ec6859b2STodor Tomov if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 542ec6859b2STodor Tomov return NULL; 543ec6859b2STodor Tomov 544ec6859b2STodor Tomov entity = pad->entity; 545ec6859b2STodor Tomov 546ec6859b2STodor Tomov if (entity->function == MEDIA_ENT_F_CAM_SENSOR) 547ec6859b2STodor Tomov return entity; 548ec6859b2STodor Tomov } 549ec6859b2STodor Tomov } 550ec6859b2STodor Tomov 55178c2cc28SAndrey Konovalov /** 55278c2cc28SAndrey Konovalov * camss_get_link_freq - Get link frequency from sensor 55378c2cc28SAndrey Konovalov * @entity: Media entity in the current pipeline 55478c2cc28SAndrey Konovalov * @bpp: Number of bits per pixel for the current format 55578c2cc28SAndrey Konovalov * @lanes: Number of lanes in the link to the sensor 55678c2cc28SAndrey Konovalov * 55778c2cc28SAndrey Konovalov * Return link frequency on success or a negative error code otherwise 55878c2cc28SAndrey Konovalov */ 55978c2cc28SAndrey Konovalov s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp, 56078c2cc28SAndrey Konovalov unsigned int lanes) 56178c2cc28SAndrey Konovalov { 56278c2cc28SAndrey Konovalov struct media_entity *sensor; 56378c2cc28SAndrey Konovalov struct v4l2_subdev *subdev; 56478c2cc28SAndrey Konovalov 56578c2cc28SAndrey Konovalov sensor = camss_find_sensor(entity); 56678c2cc28SAndrey Konovalov if (!sensor) 56778c2cc28SAndrey Konovalov return -ENODEV; 56878c2cc28SAndrey Konovalov 56978c2cc28SAndrey Konovalov subdev = media_entity_to_v4l2_subdev(sensor); 57078c2cc28SAndrey Konovalov 57178c2cc28SAndrey Konovalov return v4l2_get_link_freq(subdev->ctrl_handler, bpp, 2 * lanes); 57278c2cc28SAndrey Konovalov } 57378c2cc28SAndrey Konovalov 574ec6859b2STodor Tomov /* 575ec6859b2STodor Tomov * camss_get_pixel_clock - Get pixel clock rate from sensor 576ec6859b2STodor Tomov * @entity: Media entity in the current pipeline 577ec6859b2STodor Tomov * @pixel_clock: Received pixel clock value 578ec6859b2STodor Tomov * 579ec6859b2STodor Tomov * Return 0 on success or a negative error code otherwise 580ec6859b2STodor Tomov */ 581*2f908577SVladimir Lypak int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock) 582ec6859b2STodor Tomov { 583ec6859b2STodor Tomov struct media_entity *sensor; 584ec6859b2STodor Tomov struct v4l2_subdev *subdev; 585ec6859b2STodor Tomov struct v4l2_ctrl *ctrl; 586ec6859b2STodor Tomov 587ec6859b2STodor Tomov sensor = camss_find_sensor(entity); 588ec6859b2STodor Tomov if (!sensor) 589ec6859b2STodor Tomov return -ENODEV; 590ec6859b2STodor Tomov 591ec6859b2STodor Tomov subdev = media_entity_to_v4l2_subdev(sensor); 592ec6859b2STodor Tomov 593ec6859b2STodor Tomov ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE); 594ec6859b2STodor Tomov 595ec6859b2STodor Tomov if (!ctrl) 596ec6859b2STodor Tomov return -EINVAL; 597ec6859b2STodor Tomov 598ec6859b2STodor Tomov *pixel_clock = v4l2_ctrl_g_ctrl_int64(ctrl); 599ec6859b2STodor Tomov 600ec6859b2STodor Tomov return 0; 601ec6859b2STodor Tomov } 602ec6859b2STodor Tomov 60302afa816STodor Tomov int camss_pm_domain_on(struct camss *camss, int id) 60402afa816STodor Tomov { 6059e5d1581SAngeloGioacchino Del Regno if (camss->version == CAMSS_8x96 || 6069e5d1581SAngeloGioacchino Del Regno camss->version == CAMSS_660) { 60702afa816STodor Tomov camss->genpd_link[id] = device_link_add(camss->dev, 60802afa816STodor Tomov camss->genpd[id], DL_FLAG_STATELESS | 60902afa816STodor Tomov DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); 61002afa816STodor Tomov 61102afa816STodor Tomov if (!camss->genpd_link[id]) 61202afa816STodor Tomov return -EINVAL; 61302afa816STodor Tomov } 61402afa816STodor Tomov 61502afa816STodor Tomov return 0; 61602afa816STodor Tomov } 61702afa816STodor Tomov 61802afa816STodor Tomov void camss_pm_domain_off(struct camss *camss, int id) 61902afa816STodor Tomov { 6209e5d1581SAngeloGioacchino Del Regno if (camss->version == CAMSS_8x96 || 6219e5d1581SAngeloGioacchino Del Regno camss->version == CAMSS_660) 62202afa816STodor Tomov device_link_del(camss->genpd_link[id]); 62302afa816STodor Tomov } 62402afa816STodor Tomov 625ec6859b2STodor Tomov /* 626ec6859b2STodor Tomov * camss_of_parse_endpoint_node - Parse port endpoint node 627ec6859b2STodor Tomov * @dev: Device 628ec6859b2STodor Tomov * @node: Device node to be parsed 629ec6859b2STodor Tomov * @csd: Parsed data from port endpoint node 630ec6859b2STodor Tomov * 631ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 632ec6859b2STodor Tomov */ 633ec6859b2STodor Tomov static int camss_of_parse_endpoint_node(struct device *dev, 634ec6859b2STodor Tomov struct device_node *node, 635ec6859b2STodor Tomov struct camss_async_subdev *csd) 636ec6859b2STodor Tomov { 637ec6859b2STodor Tomov struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg; 638ec6859b2STodor Tomov struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2; 639ec6859b2STodor Tomov struct v4l2_fwnode_endpoint vep = { { 0 } }; 640ec6859b2STodor Tomov unsigned int i; 641ec6859b2STodor Tomov 642ec6859b2STodor Tomov v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); 643ec6859b2STodor Tomov 644ec6859b2STodor Tomov csd->interface.csiphy_id = vep.base.port; 645ec6859b2STodor Tomov 646ec6859b2STodor Tomov mipi_csi2 = &vep.bus.mipi_csi2; 647ec6859b2STodor Tomov lncfg->clk.pos = mipi_csi2->clock_lane; 648ec6859b2STodor Tomov lncfg->clk.pol = mipi_csi2->lane_polarities[0]; 649ec6859b2STodor Tomov lncfg->num_data = mipi_csi2->num_data_lanes; 650ec6859b2STodor Tomov 651ec6859b2STodor Tomov lncfg->data = devm_kcalloc(dev, 652ec6859b2STodor Tomov lncfg->num_data, sizeof(*lncfg->data), 653ec6859b2STodor Tomov GFP_KERNEL); 654ec6859b2STodor Tomov if (!lncfg->data) 655ec6859b2STodor Tomov return -ENOMEM; 656ec6859b2STodor Tomov 657ec6859b2STodor Tomov for (i = 0; i < lncfg->num_data; i++) { 658ec6859b2STodor Tomov lncfg->data[i].pos = mipi_csi2->data_lanes[i]; 659ec6859b2STodor Tomov lncfg->data[i].pol = mipi_csi2->lane_polarities[i + 1]; 660ec6859b2STodor Tomov } 661ec6859b2STodor Tomov 662ec6859b2STodor Tomov return 0; 663ec6859b2STodor Tomov } 664ec6859b2STodor Tomov 665ec6859b2STodor Tomov /* 666ec6859b2STodor Tomov * camss_of_parse_ports - Parse ports node 667ec6859b2STodor Tomov * @dev: Device 668ec6859b2STodor Tomov * @notifier: v4l2_device notifier data 669ec6859b2STodor Tomov * 670ec6859b2STodor Tomov * Return number of "port" nodes found in "ports" node 671ec6859b2STodor Tomov */ 672d079f94cSSteve Longerbeam static int camss_of_parse_ports(struct camss *camss) 673ec6859b2STodor Tomov { 674d079f94cSSteve Longerbeam struct device *dev = camss->dev; 675ec6859b2STodor Tomov struct device_node *node = NULL; 676ec6859b2STodor Tomov struct device_node *remote = NULL; 677d079f94cSSteve Longerbeam int ret, num_subdevs = 0; 678ec6859b2STodor Tomov 679d079f94cSSteve Longerbeam for_each_endpoint_of_node(dev->of_node, node) { 680ec6859b2STodor Tomov struct camss_async_subdev *csd; 681ec6859b2STodor Tomov 682ec6859b2STodor Tomov if (!of_device_is_available(node)) 683ec6859b2STodor Tomov continue; 684ec6859b2STodor Tomov 685ec6859b2STodor Tomov remote = of_graph_get_remote_port_parent(node); 686ec6859b2STodor Tomov if (!remote) { 687ec6859b2STodor Tomov dev_err(dev, "Cannot get remote parent\n"); 688d079f94cSSteve Longerbeam ret = -EINVAL; 689d079f94cSSteve Longerbeam goto err_cleanup; 690ec6859b2STodor Tomov } 691ec6859b2STodor Tomov 692b01edcbdSLaurent Pinchart csd = v4l2_async_notifier_add_fwnode_subdev( 693d079f94cSSteve Longerbeam &camss->notifier, of_fwnode_handle(remote), 694b01edcbdSLaurent Pinchart struct camss_async_subdev); 695016413d9SSakari Ailus of_node_put(remote); 696b01edcbdSLaurent Pinchart if (IS_ERR(csd)) { 697b01edcbdSLaurent Pinchart ret = PTR_ERR(csd); 698d079f94cSSteve Longerbeam goto err_cleanup; 699ec6859b2STodor Tomov } 700ec6859b2STodor Tomov 701d079f94cSSteve Longerbeam ret = camss_of_parse_endpoint_node(dev, node, csd); 702d079f94cSSteve Longerbeam if (ret < 0) 703d079f94cSSteve Longerbeam goto err_cleanup; 704d079f94cSSteve Longerbeam 705d079f94cSSteve Longerbeam num_subdevs++; 706d079f94cSSteve Longerbeam } 707d079f94cSSteve Longerbeam 708d079f94cSSteve Longerbeam return num_subdevs; 709d079f94cSSteve Longerbeam 710d079f94cSSteve Longerbeam err_cleanup: 711d079f94cSSteve Longerbeam of_node_put(node); 712d079f94cSSteve Longerbeam return ret; 713ec6859b2STodor Tomov } 714ec6859b2STodor Tomov 715ec6859b2STodor Tomov /* 716ec6859b2STodor Tomov * camss_init_subdevices - Initialize subdev structures and resources 717ec6859b2STodor Tomov * @camss: CAMSS device 718ec6859b2STodor Tomov * 719ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 720ec6859b2STodor Tomov */ 721ec6859b2STodor Tomov static int camss_init_subdevices(struct camss *camss) 722ec6859b2STodor Tomov { 7239c3e59deSTodor Tomov const struct resources *csiphy_res; 7249c3e59deSTodor Tomov const struct resources *csid_res; 7259c3e59deSTodor Tomov const struct resources_ispif *ispif_res; 7269c3e59deSTodor Tomov const struct resources *vfe_res; 727ec6859b2STodor Tomov unsigned int i; 728ec6859b2STodor Tomov int ret; 729ec6859b2STodor Tomov 7309c3e59deSTodor Tomov if (camss->version == CAMSS_8x16) { 7319c3e59deSTodor Tomov csiphy_res = csiphy_res_8x16; 7329c3e59deSTodor Tomov csid_res = csid_res_8x16; 7339c3e59deSTodor Tomov ispif_res = &ispif_res_8x16; 7349c3e59deSTodor Tomov vfe_res = vfe_res_8x16; 7359c3e59deSTodor Tomov } else if (camss->version == CAMSS_8x96) { 7369c3e59deSTodor Tomov csiphy_res = csiphy_res_8x96; 7379c3e59deSTodor Tomov csid_res = csid_res_8x96; 7389c3e59deSTodor Tomov ispif_res = &ispif_res_8x96; 7399c3e59deSTodor Tomov vfe_res = vfe_res_8x96; 7409e5d1581SAngeloGioacchino Del Regno } else if (camss->version == CAMSS_660) { 7419e5d1581SAngeloGioacchino Del Regno csiphy_res = csiphy_res_660; 7429e5d1581SAngeloGioacchino Del Regno csid_res = csid_res_660; 7439e5d1581SAngeloGioacchino Del Regno ispif_res = &ispif_res_660; 7449e5d1581SAngeloGioacchino Del Regno vfe_res = vfe_res_660; 7459c3e59deSTodor Tomov } else { 7469c3e59deSTodor Tomov return -EINVAL; 7479c3e59deSTodor Tomov } 7489c3e59deSTodor Tomov 7499c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) { 7509c3e59deSTodor Tomov ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i], 751ec6859b2STodor Tomov &csiphy_res[i], i); 752ec6859b2STodor Tomov if (ret < 0) { 753ec6859b2STodor Tomov dev_err(camss->dev, 754ec6859b2STodor Tomov "Failed to init csiphy%d sub-device: %d\n", 755ec6859b2STodor Tomov i, ret); 756ec6859b2STodor Tomov return ret; 757ec6859b2STodor Tomov } 758ec6859b2STodor Tomov } 759ec6859b2STodor Tomov 7609c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) { 7619c3e59deSTodor Tomov ret = msm_csid_subdev_init(camss, &camss->csid[i], 762ec6859b2STodor Tomov &csid_res[i], i); 763ec6859b2STodor Tomov if (ret < 0) { 764ec6859b2STodor Tomov dev_err(camss->dev, 765ec6859b2STodor Tomov "Failed to init csid%d sub-device: %d\n", 766ec6859b2STodor Tomov i, ret); 767ec6859b2STodor Tomov return ret; 768ec6859b2STodor Tomov } 769ec6859b2STodor Tomov } 770ec6859b2STodor Tomov 7719c3e59deSTodor Tomov ret = msm_ispif_subdev_init(&camss->ispif, ispif_res); 772ec6859b2STodor Tomov if (ret < 0) { 773ec6859b2STodor Tomov dev_err(camss->dev, "Failed to init ispif sub-device: %d\n", 774ec6859b2STodor Tomov ret); 775ec6859b2STodor Tomov return ret; 776ec6859b2STodor Tomov } 777ec6859b2STodor Tomov 7789c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) { 7799c3e59deSTodor Tomov ret = msm_vfe_subdev_init(camss, &camss->vfe[i], 7809c3e59deSTodor Tomov &vfe_res[i], i); 781ec6859b2STodor Tomov if (ret < 0) { 7829c3e59deSTodor Tomov dev_err(camss->dev, 7839c3e59deSTodor Tomov "Fail to init vfe%d sub-device: %d\n", i, ret); 784ec6859b2STodor Tomov return ret; 785ec6859b2STodor Tomov } 7869c3e59deSTodor Tomov } 787ec6859b2STodor Tomov 788ec6859b2STodor Tomov return 0; 789ec6859b2STodor Tomov } 790ec6859b2STodor Tomov 791ec6859b2STodor Tomov /* 792ec6859b2STodor Tomov * camss_register_entities - Register subdev nodes and create links 793ec6859b2STodor Tomov * @camss: CAMSS device 794ec6859b2STodor Tomov * 795ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 796ec6859b2STodor Tomov */ 797ec6859b2STodor Tomov static int camss_register_entities(struct camss *camss) 798ec6859b2STodor Tomov { 7999c3e59deSTodor Tomov int i, j, k; 800ec6859b2STodor Tomov int ret; 801ec6859b2STodor Tomov 8029c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) { 803ec6859b2STodor Tomov ret = msm_csiphy_register_entity(&camss->csiphy[i], 804ec6859b2STodor Tomov &camss->v4l2_dev); 805ec6859b2STodor Tomov if (ret < 0) { 806ec6859b2STodor Tomov dev_err(camss->dev, 807ec6859b2STodor Tomov "Failed to register csiphy%d entity: %d\n", 808ec6859b2STodor Tomov i, ret); 809ec6859b2STodor Tomov goto err_reg_csiphy; 810ec6859b2STodor Tomov } 811ec6859b2STodor Tomov } 812ec6859b2STodor Tomov 8139c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) { 814ec6859b2STodor Tomov ret = msm_csid_register_entity(&camss->csid[i], 815ec6859b2STodor Tomov &camss->v4l2_dev); 816ec6859b2STodor Tomov if (ret < 0) { 817ec6859b2STodor Tomov dev_err(camss->dev, 818ec6859b2STodor Tomov "Failed to register csid%d entity: %d\n", 819ec6859b2STodor Tomov i, ret); 820ec6859b2STodor Tomov goto err_reg_csid; 821ec6859b2STodor Tomov } 822ec6859b2STodor Tomov } 823ec6859b2STodor Tomov 824ec6859b2STodor Tomov ret = msm_ispif_register_entities(&camss->ispif, &camss->v4l2_dev); 825ec6859b2STodor Tomov if (ret < 0) { 826ec6859b2STodor Tomov dev_err(camss->dev, "Failed to register ispif entities: %d\n", 827ec6859b2STodor Tomov ret); 828ec6859b2STodor Tomov goto err_reg_ispif; 829ec6859b2STodor Tomov } 830ec6859b2STodor Tomov 8319c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) { 8329c3e59deSTodor Tomov ret = msm_vfe_register_entities(&camss->vfe[i], 8339c3e59deSTodor Tomov &camss->v4l2_dev); 834ec6859b2STodor Tomov if (ret < 0) { 8359c3e59deSTodor Tomov dev_err(camss->dev, 8369c3e59deSTodor Tomov "Failed to register vfe%d entities: %d\n", 8379c3e59deSTodor Tomov i, ret); 838ec6859b2STodor Tomov goto err_reg_vfe; 839ec6859b2STodor Tomov } 8409c3e59deSTodor Tomov } 841ec6859b2STodor Tomov 8429c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) { 8439c3e59deSTodor Tomov for (j = 0; j < camss->csid_num; j++) { 844ec6859b2STodor Tomov ret = media_create_pad_link( 845ec6859b2STodor Tomov &camss->csiphy[i].subdev.entity, 846ec6859b2STodor Tomov MSM_CSIPHY_PAD_SRC, 847ec6859b2STodor Tomov &camss->csid[j].subdev.entity, 848ec6859b2STodor Tomov MSM_CSID_PAD_SINK, 849ec6859b2STodor Tomov 0); 850ec6859b2STodor Tomov if (ret < 0) { 851ec6859b2STodor Tomov dev_err(camss->dev, 852ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 853ec6859b2STodor Tomov camss->csiphy[i].subdev.entity.name, 854ec6859b2STodor Tomov camss->csid[j].subdev.entity.name, 855ec6859b2STodor Tomov ret); 856ec6859b2STodor Tomov goto err_link; 857ec6859b2STodor Tomov } 858ec6859b2STodor Tomov } 859ec6859b2STodor Tomov } 860ec6859b2STodor Tomov 8619c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) { 8629c3e59deSTodor Tomov for (j = 0; j < camss->ispif.line_num; j++) { 863ec6859b2STodor Tomov ret = media_create_pad_link( 864ec6859b2STodor Tomov &camss->csid[i].subdev.entity, 865ec6859b2STodor Tomov MSM_CSID_PAD_SRC, 866ec6859b2STodor Tomov &camss->ispif.line[j].subdev.entity, 867ec6859b2STodor Tomov MSM_ISPIF_PAD_SINK, 868ec6859b2STodor Tomov 0); 869ec6859b2STodor Tomov if (ret < 0) { 870ec6859b2STodor Tomov dev_err(camss->dev, 871ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 872ec6859b2STodor Tomov camss->csid[i].subdev.entity.name, 873ec6859b2STodor Tomov camss->ispif.line[j].subdev.entity.name, 874ec6859b2STodor Tomov ret); 875ec6859b2STodor Tomov goto err_link; 876ec6859b2STodor Tomov } 877ec6859b2STodor Tomov } 878ec6859b2STodor Tomov } 879ec6859b2STodor Tomov 8809c3e59deSTodor Tomov for (i = 0; i < camss->ispif.line_num; i++) 8819c3e59deSTodor Tomov for (k = 0; k < camss->vfe_num; k++) 8829c3e59deSTodor Tomov for (j = 0; j < ARRAY_SIZE(camss->vfe[k].line); j++) { 883ec6859b2STodor Tomov ret = media_create_pad_link( 884ec6859b2STodor Tomov &camss->ispif.line[i].subdev.entity, 885ec6859b2STodor Tomov MSM_ISPIF_PAD_SRC, 8869c3e59deSTodor Tomov &camss->vfe[k].line[j].subdev.entity, 887ec6859b2STodor Tomov MSM_VFE_PAD_SINK, 888ec6859b2STodor Tomov 0); 889ec6859b2STodor Tomov if (ret < 0) { 890ec6859b2STodor Tomov dev_err(camss->dev, 891ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 892ec6859b2STodor Tomov camss->ispif.line[i].subdev.entity.name, 8939c3e59deSTodor Tomov camss->vfe[k].line[j].subdev.entity.name, 894ec6859b2STodor Tomov ret); 895ec6859b2STodor Tomov goto err_link; 896ec6859b2STodor Tomov } 897ec6859b2STodor Tomov } 898ec6859b2STodor Tomov 899ec6859b2STodor Tomov return 0; 900ec6859b2STodor Tomov 901ec6859b2STodor Tomov err_link: 9029c3e59deSTodor Tomov i = camss->vfe_num; 903ec6859b2STodor Tomov err_reg_vfe: 9049c3e59deSTodor Tomov for (i--; i >= 0; i--) 9059c3e59deSTodor Tomov msm_vfe_unregister_entities(&camss->vfe[i]); 9069c3e59deSTodor Tomov 907ec6859b2STodor Tomov msm_ispif_unregister_entities(&camss->ispif); 908ec6859b2STodor Tomov err_reg_ispif: 909ec6859b2STodor Tomov 9109c3e59deSTodor Tomov i = camss->csid_num; 911ec6859b2STodor Tomov err_reg_csid: 912ec6859b2STodor Tomov for (i--; i >= 0; i--) 913ec6859b2STodor Tomov msm_csid_unregister_entity(&camss->csid[i]); 914ec6859b2STodor Tomov 9159c3e59deSTodor Tomov i = camss->csiphy_num; 916ec6859b2STodor Tomov err_reg_csiphy: 917ec6859b2STodor Tomov for (i--; i >= 0; i--) 918ec6859b2STodor Tomov msm_csiphy_unregister_entity(&camss->csiphy[i]); 919ec6859b2STodor Tomov 920ec6859b2STodor Tomov return ret; 921ec6859b2STodor Tomov } 922ec6859b2STodor Tomov 923ec6859b2STodor Tomov /* 924ec6859b2STodor Tomov * camss_unregister_entities - Unregister subdev nodes 925ec6859b2STodor Tomov * @camss: CAMSS device 926ec6859b2STodor Tomov * 927ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 928ec6859b2STodor Tomov */ 929ec6859b2STodor Tomov static void camss_unregister_entities(struct camss *camss) 930ec6859b2STodor Tomov { 931ec6859b2STodor Tomov unsigned int i; 932ec6859b2STodor Tomov 9339c3e59deSTodor Tomov for (i = 0; i < camss->csiphy_num; i++) 934ec6859b2STodor Tomov msm_csiphy_unregister_entity(&camss->csiphy[i]); 935ec6859b2STodor Tomov 9369c3e59deSTodor Tomov for (i = 0; i < camss->csid_num; i++) 937ec6859b2STodor Tomov msm_csid_unregister_entity(&camss->csid[i]); 938ec6859b2STodor Tomov 939ec6859b2STodor Tomov msm_ispif_unregister_entities(&camss->ispif); 9409c3e59deSTodor Tomov 9419c3e59deSTodor Tomov for (i = 0; i < camss->vfe_num; i++) 9429c3e59deSTodor Tomov msm_vfe_unregister_entities(&camss->vfe[i]); 943ec6859b2STodor Tomov } 944ec6859b2STodor Tomov 945ec6859b2STodor Tomov static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async, 946ec6859b2STodor Tomov struct v4l2_subdev *subdev, 947ec6859b2STodor Tomov struct v4l2_async_subdev *asd) 948ec6859b2STodor Tomov { 949ec6859b2STodor Tomov struct camss *camss = container_of(async, struct camss, notifier); 950ec6859b2STodor Tomov struct camss_async_subdev *csd = 951ec6859b2STodor Tomov container_of(asd, struct camss_async_subdev, asd); 952ec6859b2STodor Tomov u8 id = csd->interface.csiphy_id; 953ec6859b2STodor Tomov struct csiphy_device *csiphy = &camss->csiphy[id]; 954ec6859b2STodor Tomov 955ec6859b2STodor Tomov csiphy->cfg.csi2 = &csd->interface.csi2; 956ec6859b2STodor Tomov subdev->host_priv = csiphy; 957ec6859b2STodor Tomov 958ec6859b2STodor Tomov return 0; 959ec6859b2STodor Tomov } 960ec6859b2STodor Tomov 961ec6859b2STodor Tomov static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async) 962ec6859b2STodor Tomov { 963ec6859b2STodor Tomov struct camss *camss = container_of(async, struct camss, notifier); 964ec6859b2STodor Tomov struct v4l2_device *v4l2_dev = &camss->v4l2_dev; 965ec6859b2STodor Tomov struct v4l2_subdev *sd; 966ec6859b2STodor Tomov int ret; 967ec6859b2STodor Tomov 968ec6859b2STodor Tomov list_for_each_entry(sd, &v4l2_dev->subdevs, list) { 969ec6859b2STodor Tomov if (sd->host_priv) { 970ec6859b2STodor Tomov struct media_entity *sensor = &sd->entity; 971ec6859b2STodor Tomov struct csiphy_device *csiphy = 972ec6859b2STodor Tomov (struct csiphy_device *) sd->host_priv; 973ec6859b2STodor Tomov struct media_entity *input = &csiphy->subdev.entity; 974ec6859b2STodor Tomov unsigned int i; 975ec6859b2STodor Tomov 976ec6859b2STodor Tomov for (i = 0; i < sensor->num_pads; i++) { 977ec6859b2STodor Tomov if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE) 978ec6859b2STodor Tomov break; 979ec6859b2STodor Tomov } 980ec6859b2STodor Tomov if (i == sensor->num_pads) { 981ec6859b2STodor Tomov dev_err(camss->dev, 982ec6859b2STodor Tomov "No source pad in external entity\n"); 983ec6859b2STodor Tomov return -EINVAL; 984ec6859b2STodor Tomov } 985ec6859b2STodor Tomov 986ec6859b2STodor Tomov ret = media_create_pad_link(sensor, i, 987ec6859b2STodor Tomov input, MSM_CSIPHY_PAD_SINK, 988ec6859b2STodor Tomov MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); 989ec6859b2STodor Tomov if (ret < 0) { 990ec6859b2STodor Tomov dev_err(camss->dev, 991ec6859b2STodor Tomov "Failed to link %s->%s entities: %d\n", 992ec6859b2STodor Tomov sensor->name, input->name, ret); 993ec6859b2STodor Tomov return ret; 994ec6859b2STodor Tomov } 995ec6859b2STodor Tomov } 996ec6859b2STodor Tomov } 997ec6859b2STodor Tomov 998ec6859b2STodor Tomov ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); 999ec6859b2STodor Tomov if (ret < 0) 1000ec6859b2STodor Tomov return ret; 1001ec6859b2STodor Tomov 1002ec6859b2STodor Tomov return media_device_register(&camss->media_dev); 1003ec6859b2STodor Tomov } 1004ec6859b2STodor Tomov 1005ec6859b2STodor Tomov static const struct v4l2_async_notifier_operations camss_subdev_notifier_ops = { 1006ec6859b2STodor Tomov .bound = camss_subdev_notifier_bound, 1007ec6859b2STodor Tomov .complete = camss_subdev_notifier_complete, 1008ec6859b2STodor Tomov }; 1009ec6859b2STodor Tomov 1010ec6859b2STodor Tomov static const struct media_device_ops camss_media_ops = { 1011ec6859b2STodor Tomov .link_notify = v4l2_pipeline_link_notify, 1012ec6859b2STodor Tomov }; 1013ec6859b2STodor Tomov 1014ec6859b2STodor Tomov /* 1015ec6859b2STodor Tomov * camss_probe - Probe CAMSS platform device 1016ec6859b2STodor Tomov * @pdev: Pointer to CAMSS platform device 1017ec6859b2STodor Tomov * 1018ec6859b2STodor Tomov * Return 0 on success or a negative error code on failure 1019ec6859b2STodor Tomov */ 1020ec6859b2STodor Tomov static int camss_probe(struct platform_device *pdev) 1021ec6859b2STodor Tomov { 1022ec6859b2STodor Tomov struct device *dev = &pdev->dev; 1023ec6859b2STodor Tomov struct camss *camss; 1024d079f94cSSteve Longerbeam int num_subdevs, ret; 1025ec6859b2STodor Tomov 1026ec6859b2STodor Tomov camss = kzalloc(sizeof(*camss), GFP_KERNEL); 1027ec6859b2STodor Tomov if (!camss) 1028ec6859b2STodor Tomov return -ENOMEM; 1029ec6859b2STodor Tomov 1030ec6859b2STodor Tomov atomic_set(&camss->ref_count, 0); 1031ec6859b2STodor Tomov camss->dev = dev; 1032ec6859b2STodor Tomov platform_set_drvdata(pdev, camss); 1033ec6859b2STodor Tomov 10349c3e59deSTodor Tomov if (of_device_is_compatible(dev->of_node, "qcom,msm8916-camss")) { 10359c3e59deSTodor Tomov camss->version = CAMSS_8x16; 10369c3e59deSTodor Tomov camss->csiphy_num = 2; 10379c3e59deSTodor Tomov camss->csid_num = 2; 10389c3e59deSTodor Tomov camss->vfe_num = 1; 10399c3e59deSTodor Tomov } else if (of_device_is_compatible(dev->of_node, 10409c3e59deSTodor Tomov "qcom,msm8996-camss")) { 10419c3e59deSTodor Tomov camss->version = CAMSS_8x96; 10429c3e59deSTodor Tomov camss->csiphy_num = 3; 10439c3e59deSTodor Tomov camss->csid_num = 4; 10449c3e59deSTodor Tomov camss->vfe_num = 2; 10459e5d1581SAngeloGioacchino Del Regno } else if (of_device_is_compatible(dev->of_node, 10469e5d1581SAngeloGioacchino Del Regno "qcom,sdm660-camss")) { 10479e5d1581SAngeloGioacchino Del Regno camss->version = CAMSS_660; 10489e5d1581SAngeloGioacchino Del Regno camss->csiphy_num = 3; 10499e5d1581SAngeloGioacchino Del Regno camss->csid_num = 4; 10509e5d1581SAngeloGioacchino Del Regno camss->vfe_num = 2; 10519c3e59deSTodor Tomov } else { 1052f45882cfSEvgeny Novikov ret = -EINVAL; 1053f45882cfSEvgeny Novikov goto err_free; 10549c3e59deSTodor Tomov } 10559c3e59deSTodor Tomov 105655b51899STodor Tomov camss->csiphy = devm_kcalloc(dev, camss->csiphy_num, 105755b51899STodor Tomov sizeof(*camss->csiphy), GFP_KERNEL); 1058f45882cfSEvgeny Novikov if (!camss->csiphy) { 1059f45882cfSEvgeny Novikov ret = -ENOMEM; 1060f45882cfSEvgeny Novikov goto err_free; 1061f45882cfSEvgeny Novikov } 10629c3e59deSTodor Tomov 106355b51899STodor Tomov camss->csid = devm_kcalloc(dev, camss->csid_num, sizeof(*camss->csid), 10649c3e59deSTodor Tomov GFP_KERNEL); 1065f45882cfSEvgeny Novikov if (!camss->csid) { 1066f45882cfSEvgeny Novikov ret = -ENOMEM; 1067f45882cfSEvgeny Novikov goto err_free; 1068f45882cfSEvgeny Novikov } 10699c3e59deSTodor Tomov 107055b51899STodor Tomov camss->vfe = devm_kcalloc(dev, camss->vfe_num, sizeof(*camss->vfe), 107155b51899STodor Tomov GFP_KERNEL); 1072f45882cfSEvgeny Novikov if (!camss->vfe) { 1073f45882cfSEvgeny Novikov ret = -ENOMEM; 1074f45882cfSEvgeny Novikov goto err_free; 1075f45882cfSEvgeny Novikov } 10769c3e59deSTodor Tomov 1077d079f94cSSteve Longerbeam v4l2_async_notifier_init(&camss->notifier); 1078d079f94cSSteve Longerbeam 1079d079f94cSSteve Longerbeam num_subdevs = camss_of_parse_ports(camss); 1080f45882cfSEvgeny Novikov if (num_subdevs < 0) { 1081f45882cfSEvgeny Novikov ret = num_subdevs; 1082f45882cfSEvgeny Novikov goto err_cleanup; 1083f45882cfSEvgeny Novikov } 1084ec6859b2STodor Tomov 1085ec6859b2STodor Tomov ret = camss_init_subdevices(camss); 1086ec6859b2STodor Tomov if (ret < 0) 1087d079f94cSSteve Longerbeam goto err_cleanup; 1088ec6859b2STodor Tomov 1089ec6859b2STodor Tomov ret = dma_set_mask_and_coherent(dev, 0xffffffff); 1090ec6859b2STodor Tomov if (ret) 1091d079f94cSSteve Longerbeam goto err_cleanup; 1092ec6859b2STodor Tomov 1093ec6859b2STodor Tomov camss->media_dev.dev = camss->dev; 1094c0decac1SMauro Carvalho Chehab strscpy(camss->media_dev.model, "Qualcomm Camera Subsystem", 1095ec6859b2STodor Tomov sizeof(camss->media_dev.model)); 1096ec6859b2STodor Tomov camss->media_dev.ops = &camss_media_ops; 1097ec6859b2STodor Tomov media_device_init(&camss->media_dev); 1098ec6859b2STodor Tomov 1099ec6859b2STodor Tomov camss->v4l2_dev.mdev = &camss->media_dev; 1100ec6859b2STodor Tomov ret = v4l2_device_register(camss->dev, &camss->v4l2_dev); 1101ec6859b2STodor Tomov if (ret < 0) { 1102ec6859b2STodor Tomov dev_err(dev, "Failed to register V4L2 device: %d\n", ret); 1103d079f94cSSteve Longerbeam goto err_cleanup; 1104ec6859b2STodor Tomov } 1105ec6859b2STodor Tomov 1106ec6859b2STodor Tomov ret = camss_register_entities(camss); 1107ec6859b2STodor Tomov if (ret < 0) 1108ec6859b2STodor Tomov goto err_register_entities; 1109ec6859b2STodor Tomov 1110d079f94cSSteve Longerbeam if (num_subdevs) { 1111ec6859b2STodor Tomov camss->notifier.ops = &camss_subdev_notifier_ops; 1112ec6859b2STodor Tomov 1113ec6859b2STodor Tomov ret = v4l2_async_notifier_register(&camss->v4l2_dev, 1114ec6859b2STodor Tomov &camss->notifier); 1115ec6859b2STodor Tomov if (ret) { 1116ec6859b2STodor Tomov dev_err(dev, 1117ec6859b2STodor Tomov "Failed to register async subdev nodes: %d\n", 1118ec6859b2STodor Tomov ret); 1119ec6859b2STodor Tomov goto err_register_subdevs; 1120ec6859b2STodor Tomov } 1121ec6859b2STodor Tomov } else { 1122ec6859b2STodor Tomov ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); 1123ec6859b2STodor Tomov if (ret < 0) { 1124ec6859b2STodor Tomov dev_err(dev, "Failed to register subdev nodes: %d\n", 1125ec6859b2STodor Tomov ret); 1126ec6859b2STodor Tomov goto err_register_subdevs; 1127ec6859b2STodor Tomov } 1128ec6859b2STodor Tomov 1129ec6859b2STodor Tomov ret = media_device_register(&camss->media_dev); 1130ec6859b2STodor Tomov if (ret < 0) { 1131ec6859b2STodor Tomov dev_err(dev, "Failed to register media device: %d\n", 1132ec6859b2STodor Tomov ret); 1133ec6859b2STodor Tomov goto err_register_subdevs; 1134ec6859b2STodor Tomov } 1135ec6859b2STodor Tomov } 1136ec6859b2STodor Tomov 11379e5d1581SAngeloGioacchino Del Regno if (camss->version == CAMSS_8x96 || 11389e5d1581SAngeloGioacchino Del Regno camss->version == CAMSS_660) { 113902afa816STodor Tomov camss->genpd[PM_DOMAIN_VFE0] = dev_pm_domain_attach_by_id( 114002afa816STodor Tomov camss->dev, PM_DOMAIN_VFE0); 114102afa816STodor Tomov if (IS_ERR(camss->genpd[PM_DOMAIN_VFE0])) 114202afa816STodor Tomov return PTR_ERR(camss->genpd[PM_DOMAIN_VFE0]); 114302afa816STodor Tomov 114402afa816STodor Tomov camss->genpd[PM_DOMAIN_VFE1] = dev_pm_domain_attach_by_id( 114502afa816STodor Tomov camss->dev, PM_DOMAIN_VFE1); 114602afa816STodor Tomov if (IS_ERR(camss->genpd[PM_DOMAIN_VFE1])) { 114702afa816STodor Tomov dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], 114802afa816STodor Tomov true); 114902afa816STodor Tomov return PTR_ERR(camss->genpd[PM_DOMAIN_VFE1]); 115002afa816STodor Tomov } 115102afa816STodor Tomov } 115202afa816STodor Tomov 115302afa816STodor Tomov pm_runtime_enable(dev); 115402afa816STodor Tomov 1155ec6859b2STodor Tomov return 0; 1156ec6859b2STodor Tomov 1157ec6859b2STodor Tomov err_register_subdevs: 1158ec6859b2STodor Tomov camss_unregister_entities(camss); 1159ec6859b2STodor Tomov err_register_entities: 1160ec6859b2STodor Tomov v4l2_device_unregister(&camss->v4l2_dev); 1161d079f94cSSteve Longerbeam err_cleanup: 1162d079f94cSSteve Longerbeam v4l2_async_notifier_cleanup(&camss->notifier); 1163f45882cfSEvgeny Novikov err_free: 1164f45882cfSEvgeny Novikov kfree(camss); 1165ec6859b2STodor Tomov 1166ec6859b2STodor Tomov return ret; 1167ec6859b2STodor Tomov } 1168ec6859b2STodor Tomov 1169ec6859b2STodor Tomov void camss_delete(struct camss *camss) 1170ec6859b2STodor Tomov { 1171ec6859b2STodor Tomov v4l2_device_unregister(&camss->v4l2_dev); 1172ec6859b2STodor Tomov media_device_unregister(&camss->media_dev); 1173ec6859b2STodor Tomov media_device_cleanup(&camss->media_dev); 1174ec6859b2STodor Tomov 117502afa816STodor Tomov pm_runtime_disable(camss->dev); 117602afa816STodor Tomov 11779e5d1581SAngeloGioacchino Del Regno if (camss->version == CAMSS_8x96 || 11789e5d1581SAngeloGioacchino Del Regno camss->version == CAMSS_660) { 117902afa816STodor Tomov dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true); 118002afa816STodor Tomov dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true); 118102afa816STodor Tomov } 118202afa816STodor Tomov 1183ec6859b2STodor Tomov kfree(camss); 1184ec6859b2STodor Tomov } 1185ec6859b2STodor Tomov 1186ec6859b2STodor Tomov /* 1187ec6859b2STodor Tomov * camss_remove - Remove CAMSS platform device 1188ec6859b2STodor Tomov * @pdev: Pointer to CAMSS platform device 1189ec6859b2STodor Tomov * 1190ec6859b2STodor Tomov * Always returns 0. 1191ec6859b2STodor Tomov */ 1192ec6859b2STodor Tomov static int camss_remove(struct platform_device *pdev) 1193ec6859b2STodor Tomov { 1194ec6859b2STodor Tomov struct camss *camss = platform_get_drvdata(pdev); 1195ec6859b2STodor Tomov 1196ec6859b2STodor Tomov v4l2_async_notifier_unregister(&camss->notifier); 1197d079f94cSSteve Longerbeam v4l2_async_notifier_cleanup(&camss->notifier); 1198ec6859b2STodor Tomov camss_unregister_entities(camss); 1199ec6859b2STodor Tomov 1200ec6859b2STodor Tomov if (atomic_read(&camss->ref_count) == 0) 1201ec6859b2STodor Tomov camss_delete(camss); 1202ec6859b2STodor Tomov 1203ec6859b2STodor Tomov return 0; 1204ec6859b2STodor Tomov } 1205ec6859b2STodor Tomov 1206ec6859b2STodor Tomov static const struct of_device_id camss_dt_match[] = { 1207ec6859b2STodor Tomov { .compatible = "qcom,msm8916-camss" }, 12089c3e59deSTodor Tomov { .compatible = "qcom,msm8996-camss" }, 12099e5d1581SAngeloGioacchino Del Regno { .compatible = "qcom,sdm660-camss" }, 1210ec6859b2STodor Tomov { } 1211ec6859b2STodor Tomov }; 1212ec6859b2STodor Tomov 1213ec6859b2STodor Tomov MODULE_DEVICE_TABLE(of, camss_dt_match); 1214ec6859b2STodor Tomov 121544a9ffd4SArnd Bergmann static int __maybe_unused camss_runtime_suspend(struct device *dev) 121602afa816STodor Tomov { 121702afa816STodor Tomov return 0; 121802afa816STodor Tomov } 121902afa816STodor Tomov 122044a9ffd4SArnd Bergmann static int __maybe_unused camss_runtime_resume(struct device *dev) 122102afa816STodor Tomov { 122202afa816STodor Tomov return 0; 122302afa816STodor Tomov } 122402afa816STodor Tomov 122502afa816STodor Tomov static const struct dev_pm_ops camss_pm_ops = { 122602afa816STodor Tomov SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 122702afa816STodor Tomov pm_runtime_force_resume) 122802afa816STodor Tomov SET_RUNTIME_PM_OPS(camss_runtime_suspend, camss_runtime_resume, NULL) 122902afa816STodor Tomov }; 123002afa816STodor Tomov 1231ec6859b2STodor Tomov static struct platform_driver qcom_camss_driver = { 1232ec6859b2STodor Tomov .probe = camss_probe, 1233ec6859b2STodor Tomov .remove = camss_remove, 1234ec6859b2STodor Tomov .driver = { 1235ec6859b2STodor Tomov .name = "qcom-camss", 1236ec6859b2STodor Tomov .of_match_table = camss_dt_match, 123702afa816STodor Tomov .pm = &camss_pm_ops, 1238ec6859b2STodor Tomov }, 1239ec6859b2STodor Tomov }; 1240ec6859b2STodor Tomov 1241ec6859b2STodor Tomov module_platform_driver(qcom_camss_driver); 1242ec6859b2STodor Tomov 1243ec6859b2STodor Tomov MODULE_ALIAS("platform:qcom-camss"); 1244ec6859b2STodor Tomov MODULE_DESCRIPTION("Qualcomm Camera Subsystem driver"); 1245ec6859b2STodor Tomov MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>"); 1246ec6859b2STodor Tomov MODULE_LICENSE("GPL v2"); 1247