18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TI Camera Access Layer (CAL) - CAMERARX 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015-2020 Texas Instruments Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: 88c2ecf20Sopenharmony_ci * Benoit Parrot <bparrot@ti.com> 98c2ecf20Sopenharmony_ci * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/regmap.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 228c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "cal.h" 268c2ecf20Sopenharmony_ci#include "cal_regs.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ 298c2ecf20Sopenharmony_ci * I/O Register Accessors 308c2ecf20Sopenharmony_ci * ------------------------------------------------------------------ 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci return ioread32(phy->base + offset); 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci iowrite32(val, phy->base + offset); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ 448c2ecf20Sopenharmony_ci * CAMERARX Management 458c2ecf20Sopenharmony_ci * ------------------------------------------------------------------ 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct v4l2_ctrl *ctrl; 518c2ecf20Sopenharmony_ci s64 rate; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); 548c2ecf20Sopenharmony_ci if (!ctrl) { 558c2ecf20Sopenharmony_ci phy_err(phy, "no pixel rate control in subdev: %s\n", 568c2ecf20Sopenharmony_ci phy->sensor->name); 578c2ecf20Sopenharmony_ci return -EPIPE; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci rate = v4l2_ctrl_g_ctrl_int64(ctrl); 618c2ecf20Sopenharmony_ci phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return rate; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void cal_camerarx_lane_config(struct cal_camerarx *phy) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)); 698c2ecf20Sopenharmony_ci u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; 708c2ecf20Sopenharmony_ci u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; 718c2ecf20Sopenharmony_ci struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = 728c2ecf20Sopenharmony_ci &phy->endpoint.bus.mipi_csi2; 738c2ecf20Sopenharmony_ci int lane; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); 768c2ecf20Sopenharmony_ci cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); 778c2ecf20Sopenharmony_ci for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * Every lane are one nibble apart starting with the 808c2ecf20Sopenharmony_ci * clock followed by the data lanes so shift masks by 4. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci lane_mask <<= 4; 838c2ecf20Sopenharmony_ci polarity_mask <<= 4; 848c2ecf20Sopenharmony_ci cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); 858c2ecf20Sopenharmony_ci cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1], 868c2ecf20Sopenharmony_ci polarity_mask); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val); 908c2ecf20Sopenharmony_ci phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", 918c2ecf20Sopenharmony_ci phy->instance, val); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void cal_camerarx_enable(struct cal_camerarx *phy) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci regmap_field_write(phy->fields[F_CAMMODE], 0); 998c2ecf20Sopenharmony_ci /* Always enable all lanes at the phy control level */ 1008c2ecf20Sopenharmony_ci regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1); 1018c2ecf20Sopenharmony_ci /* F_CSI_MODE is not present on every architecture */ 1028c2ecf20Sopenharmony_ci if (phy->fields[F_CSI_MODE]) 1038c2ecf20Sopenharmony_ci regmap_field_write(phy->fields[F_CSI_MODE], 1); 1048c2ecf20Sopenharmony_ci regmap_field_write(phy->fields[F_CTRLCLKEN], 1); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_civoid cal_camerarx_disable(struct cal_camerarx *phy) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci regmap_field_write(phy->fields[F_CTRLCLKEN], 0); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* 1138c2ecf20Sopenharmony_ci * TCLK values are OK at their reset values 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci#define TCLK_TERM 0 1168c2ecf20Sopenharmony_ci#define TCLK_MISS 1 1178c2ecf20Sopenharmony_ci#define TCLK_SETTLE 14 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate, 1208c2ecf20Sopenharmony_ci const struct cal_fmt *fmt) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci unsigned int reg0, reg1; 1238c2ecf20Sopenharmony_ci unsigned int ths_term, ths_settle; 1248c2ecf20Sopenharmony_ci unsigned int csi2_ddrclk_khz; 1258c2ecf20Sopenharmony_ci struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = 1268c2ecf20Sopenharmony_ci &phy->endpoint.bus.mipi_csi2; 1278c2ecf20Sopenharmony_ci u32 num_lanes = mipi_csi2->num_data_lanes; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* DPHY timing configuration */ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* 1328c2ecf20Sopenharmony_ci * CSI-2 is DDR and we only count used lanes. 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * csi2_ddrclk_khz = external_rate / 1000 1358c2ecf20Sopenharmony_ci * / (2 * num_lanes) * fmt->bpp; 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci csi2_ddrclk_khz = div_s64(external_rate * fmt->bpp, 1388c2ecf20Sopenharmony_ci 2 * num_lanes * 1000); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ 1438c2ecf20Sopenharmony_ci ths_term = 20 * csi2_ddrclk_khz / 1000000; 1448c2ecf20Sopenharmony_ci phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ 1478c2ecf20Sopenharmony_ci ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; 1488c2ecf20Sopenharmony_ci phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); 1518c2ecf20Sopenharmony_ci cal_set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, 1528c2ecf20Sopenharmony_ci CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); 1538c2ecf20Sopenharmony_ci cal_set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); 1548c2ecf20Sopenharmony_ci cal_set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0); 1578c2ecf20Sopenharmony_ci camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1); 1608c2ecf20Sopenharmony_ci cal_set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); 1618c2ecf20Sopenharmony_ci cal_set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); 1628c2ecf20Sopenharmony_ci cal_set_field(®1, TCLK_MISS, 1638c2ecf20Sopenharmony_ci CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); 1648c2ecf20Sopenharmony_ci cal_set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1); 1678c2ecf20Sopenharmony_ci camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void cal_camerarx_power(struct cal_camerarx *phy, bool enable) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci u32 target_state; 1738c2ecf20Sopenharmony_ci unsigned int i; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : 1768c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), 1798c2ecf20Sopenharmony_ci target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 1828c2ecf20Sopenharmony_ci u32 current_state; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci current_state = cal_read_field(phy->cal, 1858c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG(phy->instance), 1868c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (current_state == target_state) 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (i == 10) 1958c2ecf20Sopenharmony_ci phy_err(phy, "Failed to power %s complexio\n", 1968c2ecf20Sopenharmony_ci enable ? "up" : "down"); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic void cal_camerarx_wait_reset(struct cal_camerarx *phy) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci unsigned long timeout; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(750); 2048c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) { 2058c2ecf20Sopenharmony_ci if (cal_read_field(phy->cal, 2068c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG(phy->instance), 2078c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == 2088c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci usleep_range(500, 5000); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), 2148c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != 2158c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) 2168c2ecf20Sopenharmony_ci phy_err(phy, "Timeout waiting for Complex IO reset done\n"); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void cal_camerarx_wait_stop_state(struct cal_camerarx *phy) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci unsigned long timeout; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(750); 2248c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) { 2258c2ecf20Sopenharmony_ci if (cal_read_field(phy->cal, 2268c2ecf20Sopenharmony_ci CAL_CSI2_TIMING(phy->instance), 2278c2ecf20Sopenharmony_ci CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci usleep_range(500, 5000); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance), 2338c2ecf20Sopenharmony_ci CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) 2348c2ecf20Sopenharmony_ci phy_err(phy, "Timeout waiting for stop state\n"); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ciint cal_camerarx_start(struct cal_camerarx *phy, const struct cal_fmt *fmt) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci s64 external_rate; 2408c2ecf20Sopenharmony_ci u32 sscounter; 2418c2ecf20Sopenharmony_ci u32 val; 2428c2ecf20Sopenharmony_ci int ret; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci external_rate = cal_camerarx_get_external_rate(phy); 2458c2ecf20Sopenharmony_ci if (external_rate < 0) 2468c2ecf20Sopenharmony_ci return external_rate; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); 2498c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { 2508c2ecf20Sopenharmony_ci phy_err(phy, "power on failed in subdev\n"); 2518c2ecf20Sopenharmony_ci return ret; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* 2558c2ecf20Sopenharmony_ci * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP / 2568c2ecf20Sopenharmony_ci * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x / 2578c2ecf20Sopenharmony_ci * DRA80xM TRMs have a a slightly simplified sequence. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * 1. Configure all CSI-2 low level protocol registers to be ready to 2628c2ecf20Sopenharmony_ci * receive signals/data from the CSI-2 PHY. 2638c2ecf20Sopenharmony_ci * 2648c2ecf20Sopenharmony_ci * i.-v. Configure the lanes position and polarity. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci cal_camerarx_lane_config(phy); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* 2698c2ecf20Sopenharmony_ci * vi.-vii. Configure D-PHY mode, enable the required lanes and 2708c2ecf20Sopenharmony_ci * enable the CAMERARX clock. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_ci cal_camerarx_enable(phy); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * 2. CSI PHY and link initialization sequence. 2768c2ecf20Sopenharmony_ci * 2778c2ecf20Sopenharmony_ci * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion 2788c2ecf20Sopenharmony_ci * at this point, as it requires the external sensor to send the 2798c2ecf20Sopenharmony_ci * CSI-2 HS clock. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), 2828c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, 2838c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); 2848c2ecf20Sopenharmony_ci phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", 2858c2ecf20Sopenharmony_ci phy->instance, 2868c2ecf20Sopenharmony_ci cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Dummy read to allow SCP reset to complete. */ 2898c2ecf20Sopenharmony_ci camerarx_read(phy, CAL_CSI2_PHY_REG0); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* Program the PHY timing parameters. */ 2928c2ecf20Sopenharmony_ci cal_camerarx_config(phy, external_rate, fmt); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* 2958c2ecf20Sopenharmony_ci * b. Assert the FORCERXMODE signal. 2968c2ecf20Sopenharmony_ci * 2978c2ecf20Sopenharmony_ci * The stop-state-counter is based on fclk cycles, and we always use 2988c2ecf20Sopenharmony_ci * the x16 and x4 settings, so stop-state-timeout = 2998c2ecf20Sopenharmony_ci * fclk-cycle * 16 * 4 * counter. 3008c2ecf20Sopenharmony_ci * 3018c2ecf20Sopenharmony_ci * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we 3028c2ecf20Sopenharmony_ci * calculate a timeout that's 100us (rounding up). 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_ci sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 * 16 * 4); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)); 3078c2ecf20Sopenharmony_ci cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); 3088c2ecf20Sopenharmony_ci cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); 3098c2ecf20Sopenharmony_ci cal_set_field(&val, sscounter, 3108c2ecf20Sopenharmony_ci CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); 3118c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val); 3128c2ecf20Sopenharmony_ci phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", 3138c2ecf20Sopenharmony_ci phy->instance, 3148c2ecf20Sopenharmony_ci cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Assert the FORCERXMODE signal. */ 3178c2ecf20Sopenharmony_ci cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance), 3188c2ecf20Sopenharmony_ci 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); 3198c2ecf20Sopenharmony_ci phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", 3208c2ecf20Sopenharmony_ci phy->instance, 3218c2ecf20Sopenharmony_ci cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance))); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* 3248c2ecf20Sopenharmony_ci * c. Connect pull-down on CSI-2 PHY link (using pad control). 3258c2ecf20Sopenharmony_ci * 3268c2ecf20Sopenharmony_ci * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not 3278c2ecf20Sopenharmony_ci * implemented. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* 3318c2ecf20Sopenharmony_ci * d. Power up the CSI-2 PHY. 3328c2ecf20Sopenharmony_ci * e. Check whether the state status reaches the ON state. 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_ci cal_camerarx_power(phy, true); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* 3378c2ecf20Sopenharmony_ci * Start the sensor to enable the CSI-2 HS clock. We can now wait for 3388c2ecf20Sopenharmony_ci * CSI-2 PHY reset to complete. 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); 3418c2ecf20Sopenharmony_ci if (ret) { 3428c2ecf20Sopenharmony_ci v4l2_subdev_call(phy->sensor, core, s_power, 0); 3438c2ecf20Sopenharmony_ci phy_err(phy, "stream on failed in subdev\n"); 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci cal_camerarx_wait_reset(phy); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* f. Wait for STOPSTATE=1 for all enabled lane modules. */ 3508c2ecf20Sopenharmony_ci cal_camerarx_wait_stop_state(phy); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n", 3538c2ecf20Sopenharmony_ci phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1)); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* 3568c2ecf20Sopenharmony_ci * g. Disable pull-down on CSI-2 PHY link (using pad control). 3578c2ecf20Sopenharmony_ci * 3588c2ecf20Sopenharmony_ci * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not 3598c2ecf20Sopenharmony_ci * implemented. 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_civoid cal_camerarx_stop(struct cal_camerarx *phy) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci unsigned int i; 3688c2ecf20Sopenharmony_ci int ret; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci cal_camerarx_power(phy, false); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* Assert Complex IO Reset */ 3738c2ecf20Sopenharmony_ci cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), 3748c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, 3758c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* Wait for power down completion */ 3788c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 3798c2ecf20Sopenharmony_ci if (cal_read_field(phy->cal, 3808c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG(phy->instance), 3818c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == 3828c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", 3878c2ecf20Sopenharmony_ci phy->instance, 3888c2ecf20Sopenharmony_ci cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, 3898c2ecf20Sopenharmony_ci (i >= 10) ? "(timeout)" : ""); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* Disable the phy */ 3928c2ecf20Sopenharmony_ci cal_camerarx_disable(phy); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (v4l2_subdev_call(phy->sensor, video, s_stream, 0)) 3958c2ecf20Sopenharmony_ci phy_err(phy, "stream off failed in subdev\n"); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(phy->sensor, core, s_power, 0); 3988c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) 3998c2ecf20Sopenharmony_ci phy_err(phy, "power off failed in subdev\n"); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci/* 4038c2ecf20Sopenharmony_ci * Errata i913: CSI2 LDO Needs to be disabled when module is powered on 4048c2ecf20Sopenharmony_ci * 4058c2ecf20Sopenharmony_ci * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 4068c2ecf20Sopenharmony_ci * LDOs on the device are disabled if CSI-2 module is powered on 4078c2ecf20Sopenharmony_ci * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 4088c2ecf20Sopenharmony_ci * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high 4098c2ecf20Sopenharmony_ci * current draw on the module supply in active mode. 4108c2ecf20Sopenharmony_ci * 4118c2ecf20Sopenharmony_ci * Errata does not apply when CSI-2 module is powered off 4128c2ecf20Sopenharmony_ci * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). 4138c2ecf20Sopenharmony_ci * 4148c2ecf20Sopenharmony_ci * SW Workaround: 4158c2ecf20Sopenharmony_ci * Set the following register bits to disable the LDO, 4168c2ecf20Sopenharmony_ci * which is essentially CSI2 REG10 bit 6: 4178c2ecf20Sopenharmony_ci * 4188c2ecf20Sopenharmony_ci * Core 0: 0x4845 B828 = 0x0000 0040 4198c2ecf20Sopenharmony_ci * Core 1: 0x4845 B928 = 0x0000 0040 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_civoid cal_camerarx_i913_errata(struct cal_camerarx *phy) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci cal_set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10); 4288c2ecf20Sopenharmony_ci camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10); 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci/* 4328c2ecf20Sopenharmony_ci * Enable the expected IRQ sources 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_civoid cal_camerarx_enable_irqs(struct cal_camerarx *phy) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci u32 val; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci const u32 cio_err_mask = 4398c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | 4408c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | 4418c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | 4428c2ecf20Sopenharmony_ci CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Enable CIO error irqs */ 4458c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), 4468c2ecf20Sopenharmony_ci CAL_HL_IRQ_CIO_MASK(phy->instance)); 4478c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 4488c2ecf20Sopenharmony_ci cio_err_mask); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* Always enable OCPO error */ 4518c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* Enable IRQ_WDMA_END 0/1 */ 4548c2ecf20Sopenharmony_ci val = 0; 4558c2ecf20Sopenharmony_ci cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); 4568c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_HL_IRQENABLE_SET(1), val); 4578c2ecf20Sopenharmony_ci /* Enable IRQ_WDMA_START 0/1 */ 4588c2ecf20Sopenharmony_ci val = 0; 4598c2ecf20Sopenharmony_ci cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); 4608c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_HL_IRQENABLE_SET(2), val); 4618c2ecf20Sopenharmony_ci /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ 4628c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0xFF000000); 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_civoid cal_camerarx_disable_irqs(struct cal_camerarx *phy) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci u32 val; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* Disable CIO error irqs */ 4708c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), 4718c2ecf20Sopenharmony_ci CAL_HL_IRQ_CIO_MASK(phy->instance)); 4728c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* Disable IRQ_WDMA_END 0/1 */ 4758c2ecf20Sopenharmony_ci val = 0; 4768c2ecf20Sopenharmony_ci cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); 4778c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(1), val); 4788c2ecf20Sopenharmony_ci /* Disable IRQ_WDMA_START 0/1 */ 4798c2ecf20Sopenharmony_ci val = 0; 4808c2ecf20Sopenharmony_ci cal_set_field(&val, 1, CAL_HL_IRQ_MASK(phy->instance)); 4818c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(2), val); 4828c2ecf20Sopenharmony_ci /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ 4838c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(0), 0); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_civoid cal_camerarx_ppi_enable(struct cal_camerarx *phy) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci cal_write(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), BIT(3)); 4898c2ecf20Sopenharmony_ci cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 4908c2ecf20Sopenharmony_ci 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_civoid cal_camerarx_ppi_disable(struct cal_camerarx *phy) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 4968c2ecf20Sopenharmony_ci 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic int cal_camerarx_regmap_init(struct cal_dev *cal, 5008c2ecf20Sopenharmony_ci struct cal_camerarx *phy) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci const struct cal_camerarx_data *phy_data; 5038c2ecf20Sopenharmony_ci unsigned int i; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (!cal->data) 5068c2ecf20Sopenharmony_ci return -EINVAL; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci phy_data = &cal->data->camerarx[phy->instance]; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci for (i = 0; i < F_MAX_FIELDS; i++) { 5118c2ecf20Sopenharmony_ci struct reg_field field = { 5128c2ecf20Sopenharmony_ci .reg = cal->syscon_camerrx_offset, 5138c2ecf20Sopenharmony_ci .lsb = phy_data->fields[i].lsb, 5148c2ecf20Sopenharmony_ci .msb = phy_data->fields[i].msb, 5158c2ecf20Sopenharmony_ci }; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* 5188c2ecf20Sopenharmony_ci * Here we update the reg offset with the 5198c2ecf20Sopenharmony_ci * value found in DT 5208c2ecf20Sopenharmony_ci */ 5218c2ecf20Sopenharmony_ci phy->fields[i] = devm_regmap_field_alloc(cal->dev, 5228c2ecf20Sopenharmony_ci cal->syscon_camerrx, 5238c2ecf20Sopenharmony_ci field); 5248c2ecf20Sopenharmony_ci if (IS_ERR(phy->fields[i])) { 5258c2ecf20Sopenharmony_ci cal_err(cal, "Unable to allocate regmap fields\n"); 5268c2ecf20Sopenharmony_ci return PTR_ERR(phy->fields[i]); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic int cal_camerarx_parse_dt(struct cal_camerarx *phy) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint; 5368c2ecf20Sopenharmony_ci struct device_node *ep_node; 5378c2ecf20Sopenharmony_ci char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2]; 5388c2ecf20Sopenharmony_ci unsigned int i; 5398c2ecf20Sopenharmony_ci int ret; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* 5428c2ecf20Sopenharmony_ci * Find the endpoint node for the port corresponding to the PHY 5438c2ecf20Sopenharmony_ci * instance, and parse its CSI-2-related properties. 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_ci ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node, 5468c2ecf20Sopenharmony_ci phy->instance, 0); 5478c2ecf20Sopenharmony_ci if (!ep_node) { 5488c2ecf20Sopenharmony_ci /* 5498c2ecf20Sopenharmony_ci * The endpoint is not mandatory, not all PHY instances need to 5508c2ecf20Sopenharmony_ci * be connected in DT. 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_ci phy_dbg(3, phy, "Port has no endpoint\n"); 5538c2ecf20Sopenharmony_ci return 0; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; 5578c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); 5588c2ecf20Sopenharmony_ci if (ret < 0) { 5598c2ecf20Sopenharmony_ci phy_err(phy, "Failed to parse endpoint\n"); 5608c2ecf20Sopenharmony_ci goto done; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) { 5648c2ecf20Sopenharmony_ci unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i]; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (lane > 4) { 5678c2ecf20Sopenharmony_ci phy_err(phy, "Invalid position %u for data lane %u\n", 5688c2ecf20Sopenharmony_ci lane, i); 5698c2ecf20Sopenharmony_ci ret = -EINVAL; 5708c2ecf20Sopenharmony_ci goto done; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci data_lanes[i*2] = '0' + lane; 5748c2ecf20Sopenharmony_ci data_lanes[i*2+1] = ' '; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci data_lanes[i*2-1] = '\0'; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci phy_dbg(3, phy, 5808c2ecf20Sopenharmony_ci "CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n", 5818c2ecf20Sopenharmony_ci endpoint->bus.mipi_csi2.clock_lane, data_lanes, 5828c2ecf20Sopenharmony_ci endpoint->bus.mipi_csi2.flags); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Retrieve the connected device and store it for later use. */ 5858c2ecf20Sopenharmony_ci phy->sensor_node = of_graph_get_remote_port_parent(ep_node); 5868c2ecf20Sopenharmony_ci if (!phy->sensor_node) { 5878c2ecf20Sopenharmony_ci phy_dbg(3, phy, "Can't get remote parent\n"); 5888c2ecf20Sopenharmony_ci ret = -EINVAL; 5898c2ecf20Sopenharmony_ci goto done; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cidone: 5958c2ecf20Sopenharmony_ci of_node_put(ep_node); 5968c2ecf20Sopenharmony_ci return ret; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistruct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, 6008c2ecf20Sopenharmony_ci unsigned int instance) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(cal->dev); 6038c2ecf20Sopenharmony_ci struct cal_camerarx *phy; 6048c2ecf20Sopenharmony_ci int ret; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci phy = kzalloc(sizeof(*phy), GFP_KERNEL); 6078c2ecf20Sopenharmony_ci if (!phy) 6088c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci phy->cal = cal; 6118c2ecf20Sopenharmony_ci phy->instance = instance; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 6148c2ecf20Sopenharmony_ci (instance == 0) ? 6158c2ecf20Sopenharmony_ci "cal_rx_core0" : 6168c2ecf20Sopenharmony_ci "cal_rx_core1"); 6178c2ecf20Sopenharmony_ci phy->base = devm_ioremap_resource(cal->dev, phy->res); 6188c2ecf20Sopenharmony_ci if (IS_ERR(phy->base)) { 6198c2ecf20Sopenharmony_ci cal_err(cal, "failed to ioremap\n"); 6208c2ecf20Sopenharmony_ci ret = PTR_ERR(phy->base); 6218c2ecf20Sopenharmony_ci goto error; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", 6258c2ecf20Sopenharmony_ci phy->res->name, &phy->res->start, &phy->res->end); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci ret = cal_camerarx_regmap_init(cal, phy); 6288c2ecf20Sopenharmony_ci if (ret) 6298c2ecf20Sopenharmony_ci goto error; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci ret = cal_camerarx_parse_dt(phy); 6328c2ecf20Sopenharmony_ci if (ret) 6338c2ecf20Sopenharmony_ci goto error; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci return phy; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cierror: 6388c2ecf20Sopenharmony_ci kfree(phy); 6398c2ecf20Sopenharmony_ci return ERR_PTR(ret); 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_civoid cal_camerarx_destroy(struct cal_camerarx *phy) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci if (!phy) 6458c2ecf20Sopenharmony_ci return; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci of_node_put(phy->sensor_node); 6488c2ecf20Sopenharmony_ci kfree(phy); 6498c2ecf20Sopenharmony_ci} 650