// SPDX-License-Identifier: GPL-2.0 /* * Maxim Dual GMSL Deserializer MIPI txphy driver * * Copyright (C) 2023 Rockchip Electronics Co., Ltd. * * Author: Cai Wenzhong * */ #include #include "maxim2c_api.h" static int maxim2c_txphy_auto_init_deskew(maxim2c_t *maxim2c) { struct i2c_client *client = maxim2c->client; maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; struct maxim2c_txphy_cfg *phy_cfg = NULL; u16 reg_addr = 0; u8 phy_idx = 0; int ret = 0; // D-PHY Deskew Initial Calibration Control for (phy_idx = 0; phy_idx < MAXIM2C_TXPHY_ID_MAX; phy_idx++) { // Auto Deskew can only be configured on PHY1 and PHY2 if ((phy_idx == MAXIM2C_TXPHY_ID_A) || (phy_idx == MAXIM2C_TXPHY_ID_D)) continue; phy_cfg = &mipi_txphy->phy_cfg[phy_idx]; if (phy_cfg->phy_enable && (phy_cfg->auto_deskew & BIT(7))) { reg_addr = 0x0403 + 0x40 * phy_idx; ret |= maxim2c_i2c_write_byte(client, reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, phy_cfg->auto_deskew); } } return ret; } static int maxim2c_mipi_txphy_lane_mapping(maxim2c_t *maxim2c) { struct i2c_client *client = maxim2c->client; maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; struct maxim2c_txphy_cfg *phy_cfg = NULL; u8 reg_value = 0, reg_mask = 0; int ret = 0; // MIPI TXPHY A/B: data lane mapping reg_mask = 0; reg_value = 0; phy_cfg = &mipi_txphy->phy_cfg[MAXIM2C_TXPHY_ID_A]; if (phy_cfg->phy_enable) { reg_mask |= 0x0F; reg_value |= (phy_cfg->data_lane_map << 0); } phy_cfg = &mipi_txphy->phy_cfg[MAXIM2C_TXPHY_ID_B]; if (phy_cfg->phy_enable) { reg_mask |= 0xF0; reg_value |= (phy_cfg->data_lane_map << 4); } if (reg_mask != 0) { ret |= maxim2c_i2c_update_byte(client, 0x0333, MAXIM2C_I2C_REG_ADDR_16BITS, reg_mask, reg_value); } // MIPI TXPHY C/D: data lane mapping reg_mask = 0; reg_value = 0; phy_cfg = &mipi_txphy->phy_cfg[MAXIM2C_TXPHY_ID_C]; if (phy_cfg->phy_enable) { reg_mask |= 0x0F; reg_value |= (phy_cfg->data_lane_map << 0); } phy_cfg = &mipi_txphy->phy_cfg[MAXIM2C_TXPHY_ID_D]; if (phy_cfg->phy_enable) { reg_mask |= 0xF0; reg_value |= (phy_cfg->data_lane_map << 4); } if (reg_mask != 0) { ret |= maxim2c_i2c_update_byte(client, 0x0334, MAXIM2C_I2C_REG_ADDR_16BITS, reg_mask, reg_value); } return ret; } static int maxim2c_mipi_txphy_type_vcx_lane_num(maxim2c_t *maxim2c) { struct i2c_client *client = maxim2c->client; maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; struct maxim2c_txphy_cfg *phy_cfg = NULL; u8 phy_idx = 0; u8 reg_mask = 0, reg_value = 0; u16 reg_addr = 0; int ret = 0; for (phy_idx = 0; phy_idx < MAXIM2C_TXPHY_ID_MAX; phy_idx++) { phy_cfg = &mipi_txphy->phy_cfg[phy_idx]; if (phy_cfg->phy_enable == 0) continue; reg_mask = BIT(7) | BIT(6) | BIT(5) | BIT(3); reg_value = 0; if (phy_cfg->phy_type == MAXIM2C_TXPHY_TYPE_CPHY) reg_value |= BIT(5); if (phy_cfg->vc_ext_en) reg_value |= BIT(3); reg_value |= ((phy_cfg->data_lane_num - 1) << 6); reg_addr = 0x040A + 0x40 * phy_idx; ret |= maxim2c_i2c_update_byte(client, reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, reg_mask, reg_value); } return ret; } static int maxim2c_mipi_txphy_tunnel_init(maxim2c_t *maxim2c) { struct i2c_client *client = maxim2c->client; maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; struct maxim2c_txphy_cfg *phy_cfg = NULL; u8 phy_idx = 0; u8 reg_mask = 0, reg_value = 0; u16 reg_addr = 0; int ret = 0; for (phy_idx = 0; phy_idx < MAXIM2C_TXPHY_ID_MAX; phy_idx++) { // Tunnel mode can only be configured on PHY1 and PHY2 if ((phy_idx == MAXIM2C_TXPHY_ID_A) || (phy_idx == MAXIM2C_TXPHY_ID_D)) continue; phy_cfg = &mipi_txphy->phy_cfg[phy_idx]; if (phy_cfg->tunnel_enable) { // tunnel mode: enable reg_mask = BIT(0); reg_value = BIT(0); // tunnel pipe destination reg_mask |= BIT(1); reg_value |= ((phy_cfg->tunnel_dest & 0x1) << 1); } else { // tunnel mode: disable reg_mask = BIT(0); reg_value = 0; } reg_addr = 0x0434 + 0x40 * phy_idx; ret |= maxim2c_i2c_update_byte(client, reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, reg_mask, reg_value); if (phy_cfg->tunnel_enable) { reg_addr = 0x0433 + 0x40 * phy_idx; reg_mask = (BIT(7) | BIT(6) | BIT(5)); reg_value = ((phy_cfg->tunnel_vs_wait & 0x07) << 5); ret |= maxim2c_i2c_update_byte(client, reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, reg_mask, reg_value); } } return ret; } int maxim2c_mipi_txphy_enable(maxim2c_t *maxim2c, bool enable) { struct i2c_client *client = maxim2c->client; struct device *dev = &client->dev; maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; u8 phy_idx = 0; u8 reg_mask = 0, reg_value = 0; int ret = 0; dev_dbg(dev, "%s: enable = %d\n", __func__, enable); reg_mask = 0xF0; reg_value = 0; if (enable) { for (phy_idx = 0; phy_idx < MAXIM2C_TXPHY_ID_MAX; phy_idx++) { if (mipi_txphy->phy_cfg[phy_idx].phy_enable) reg_value |= BIT(4 + phy_idx); } } ret |= maxim2c_i2c_update_byte(client, 0x0332, MAXIM2C_I2C_REG_ADDR_16BITS, reg_mask, reg_value); return ret; } int maxim2c_dphy_dpll_predef_set(maxim2c_t *maxim2c, s64 link_freq_hz) { struct i2c_client *client = maxim2c->client; struct device *dev = &client->dev; maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; struct maxim2c_txphy_cfg *phy_cfg = NULL; u32 link_freq_mhz = 0; u16 reg_addr = 0; u8 phy_idx = 0; u8 dpll_mask = 0, dpll_val = 0, dpll_lock = 0; int ret = 0; dpll_mask = 0; link_freq_mhz = (u32)div_s64(link_freq_hz, 1000000L); dpll_val = DIV_ROUND_UP(link_freq_mhz * 2, 100) & 0x1F; if (dpll_val == 0) dpll_val = 15; /* default 1500MBps */ // Disable software override for frequency fine tuning dpll_val |= BIT(5); for (phy_idx = 0; phy_idx < MAXIM2C_TXPHY_ID_MAX; phy_idx++) { phy_cfg = &mipi_txphy->phy_cfg[phy_idx]; if ((phy_cfg->phy_enable == 0) || (phy_cfg->clock_master == 0)) continue; if (phy_cfg->clock_mode != MAXIM2C_TXPHY_DPLL_PREDEF) continue; dpll_mask |= BIT(phy_idx + 4); // Hold DPLL in reset (config_soft_rst_n = 0) before changing the rate reg_addr = 0x1C00 + 0x100 * phy_idx; ret |= maxim2c_i2c_write_byte(client, reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, 0xf4); // Set dpll data rate reg_addr = 0x031D + 0x03 * phy_idx; ret |= maxim2c_i2c_update_byte(client, reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, 0x3F, dpll_val); // Release reset to DPLL (config_soft_rst_n = 1) reg_addr = 0x1C00 + 0x100 * phy_idx; ret |= maxim2c_i2c_write_byte(client, reg_addr, MAXIM2C_I2C_REG_ADDR_16BITS, 0xf5); } if (ret) { dev_err(dev, "DPLL predef set error!\n"); return ret; } #if 0 ret = read_poll_timeout(maxim2c_i2c_read_byte, ret, !(ret < 0) && (dpll_lock & dpll_mask), 1000, 10000, false, client, 0x0308, MAXIM2C_I2C_REG_ADDR_16BITS, &dpll_lock); if (ret < 0) { dev_err(dev, "DPLL is unlocked: 0x%02x\n", dpll_lock); return ret; } else { dev_info(dev, "DPLL is locked: 0x%02x\n", dpll_lock); return 0; } #else // The locking status of DPLL cannot be obtained before csi output usleep_range(1000, 1100); ret = maxim2c_i2c_read_byte(client, 0x0308, MAXIM2C_I2C_REG_ADDR_16BITS, &dpll_lock); dev_info(dev, "DPLL lock state: 0x%02x\n", dpll_lock); return ret; #endif } EXPORT_SYMBOL(maxim2c_dphy_dpll_predef_set); int maxim2c_mipi_csi_output(maxim2c_t *maxim2c, bool enable) { struct i2c_client *client = maxim2c->client; struct device *dev = &client->dev; maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; u8 reg_mask = 0, reg_value = 0; int ret = 0; dev_dbg(dev, "%s: enable = %d\n", __func__, enable); if (mipi_txphy->force_clock_out_en != 0) { reg_mask = BIT(7); reg_value = enable ? BIT(7) : 0; // Force all MIPI clocks running Config ret |= maxim2c_i2c_update_byte(client, 0x0330, MAXIM2C_I2C_REG_ADDR_16BITS, reg_mask, reg_value); } /* Bit1 of the register 0x0313: CSI_OUT_EN * 1 = CSI output enabled * 0 = CSI output disabled */ reg_mask = BIT(1); reg_value = enable ? BIT(1) : 0; // MIPI CSI output Setting ret |= maxim2c_i2c_update_byte(client, 0x0313, MAXIM2C_I2C_REG_ADDR_16BITS, reg_mask, reg_value); return ret; } EXPORT_SYMBOL(maxim2c_mipi_csi_output); static int maxim2c_mipi_txphy_config_parse_dt(struct device *dev, maxim2c_mipi_txphy_t *mipi_txphy, struct device_node *parent_node) { struct device_node *node = NULL; struct maxim2c_txphy_cfg *phy_cfg = NULL; const char *txphy_cfg_name = "mipi-txphy-config"; u32 value = 0; u32 sub_idx = 0, phy_id = 0; int ret; node = NULL; sub_idx = 0; while ((node = of_get_next_child(parent_node, node))) { if (!strncasecmp(node->name, txphy_cfg_name, strlen(txphy_cfg_name))) { if (sub_idx >= MAXIM2C_TXPHY_ID_MAX) { dev_err(dev, "%pOF: Too many matching %s node\n", parent_node, txphy_cfg_name); of_node_put(node); break; } if (!of_device_is_available(node)) { dev_info(dev, "%pOF is disabled\n", node); sub_idx++; continue; } /* MIPI TXPHY: phy id */ ret = of_property_read_u32(node, "phy-id", &phy_id); if (ret) { // if mipi txphy phy_id is error, parse next node dev_err(dev, "Can not get phy-id property!"); sub_idx++; continue; } if (phy_id >= MAXIM2C_TXPHY_ID_MAX) { // if mipi txphy phy_id is error, parse next node dev_err(dev, "Error phy-id = %d!", phy_id); sub_idx++; continue; } phy_cfg = &mipi_txphy->phy_cfg[phy_id]; /* MIPI TXPHY: phy enable */ phy_cfg->phy_enable = 1; dev_info(dev, "mipi txphy id = %d: phy_enable = %d\n", phy_id, phy_cfg->phy_enable); /* MIPI TXPHY: other config */ ret = of_property_read_u32(node, "phy-type", &value); if (ret == 0) { dev_info(dev, "phy-type property: %d", value); phy_cfg->phy_type = value; } ret = of_property_read_u32(node, "auto-deskew", &value); if (ret == 0) { dev_info(dev, "auto-deskew property: 0x%x", value); phy_cfg->auto_deskew = value; } ret = of_property_read_u32(node, "data-lane-num", &value); if (ret == 0) { dev_info(dev, "data-lane-num property: %d", value); phy_cfg->data_lane_num = value; } ret = of_property_read_u32(node, "data-lane-map", &value); if (ret == 0) { dev_info(dev, "data-lane-map property: 0x%x", value); phy_cfg->data_lane_map = value; } ret = of_property_read_u32(node, "vc-ext-en", &value); if (ret == 0) { dev_info(dev, "vc-ext-en property: %d", value); phy_cfg->vc_ext_en = value; } ret = of_property_read_u32(node, "tunnel-enable", &value); if (ret == 0) { dev_info(dev, "tunnel-enable property: %d", value); phy_cfg->tunnel_enable = value; } ret = of_property_read_u32(node, "tunnel-vs-wait", &value); if (ret == 0) { dev_info(dev, "tunnel-vs-wait property: %d", value); phy_cfg->tunnel_vs_wait = value; } ret = of_property_read_u32(node, "tunnel-dest", &value); if (ret == 0) { dev_info(dev, "tunnel-dest property: %d", value); phy_cfg->tunnel_dest = value; } ret = of_property_read_u32(node, "clock-mode", &value); if (ret == 0) { dev_info(dev, "clock-mode property: %d", value); phy_cfg->clock_mode = value; } sub_idx++; } } return 0; } int maxim2c_mipi_txphy_parse_dt(maxim2c_t *maxim2c, struct device_node *of_node) { struct device *dev = &maxim2c->client->dev; struct device_node *node = NULL; maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; u32 value = 0; int ret = 0; dev_info(dev, "=== maxim2c mipi txphy parse dt ===\n"); node = of_get_child_by_name(of_node, "mipi-txphys"); if (IS_ERR_OR_NULL(node)) { dev_err(dev, "%pOF has no child node: mipi-txphys\n", of_node); return -ENODEV; } if (!of_device_is_available(node)) { dev_info(dev, "%pOF is disabled\n", node); of_node_put(node); return -ENODEV; } /* mipi txphy mode */ ret = of_property_read_u32(node, "phy-mode", &value); if (ret == 0) { dev_info(dev, "phy-mode property: %d\n", value); mipi_txphy->phy_mode = value; } dev_info(dev, "mipi txphy mode: %d\n", mipi_txphy->phy_mode); /* MIPI clocks running mode */ ret = of_property_read_u32(node, "phy-force-clock-out", &value); if (ret == 0) { dev_info(dev, "phy-force-clock-out property: %d\n", value); mipi_txphy->force_clock_out_en = value; } dev_info(dev, "mipi txphy force clock out enable: %d\n", mipi_txphy->force_clock_out_en); ret = maxim2c_mipi_txphy_config_parse_dt(dev, mipi_txphy, node); of_node_put(node); return ret; } EXPORT_SYMBOL(maxim2c_mipi_txphy_parse_dt); int maxim2c_mipi_txphy_hw_init(maxim2c_t *maxim2c) { struct i2c_client *client = maxim2c->client; struct device *dev = &client->dev; maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; struct maxim2c_txphy_cfg *phy_cfg = NULL; u8 mode = 0; int ret = 0; switch (mipi_txphy->phy_mode) { case MAXIM2C_TXPHY_MODE_2X2LANES: mode = BIT(0); break; case MAXIM2C_TXPHY_MODE_2X4LANES: default: mode = BIT(2); break; } // clock master phy_cfg = &mipi_txphy->phy_cfg[MAXIM2C_TXPHY_ID_B]; if (phy_cfg->phy_enable) { if (phy_cfg->tunnel_enable) { if (phy_cfg->tunnel_dest == 0) { phy_cfg->clock_master = 1; } else { phy_cfg->phy_enable = 0; phy_cfg->clock_master = 0; } } else { phy_cfg->clock_master = 1; } } phy_cfg = &mipi_txphy->phy_cfg[MAXIM2C_TXPHY_ID_C]; if (phy_cfg->phy_enable) { if (phy_cfg->tunnel_enable) { if (phy_cfg->tunnel_dest == 1) { phy_cfg->clock_master = 1; } else { phy_cfg->phy_enable = 0; phy_cfg->clock_master = 0; } } else { phy_cfg->clock_master = 1; } } // MIPI TXPHY Mode setting ret |= maxim2c_i2c_write_byte(client, 0x0330, MAXIM2C_I2C_REG_ADDR_16BITS, mode); // Waits for a frame before generating MIPI Packet requests to the MIPI TX ret |= maxim2c_i2c_update_byte(client, 0x0325, MAXIM2C_I2C_REG_ADDR_16BITS, BIT(7), BIT(7)); // mipi txphy data lane mapping ret |= maxim2c_mipi_txphy_lane_mapping(maxim2c); // mipi txphy type, lane number, virtual channel extension ret |= maxim2c_mipi_txphy_type_vcx_lane_num(maxim2c); // mipi txphy tunnel init ret |= maxim2c_mipi_txphy_tunnel_init(maxim2c); // mipi txphy auto init deskew ret |= maxim2c_txphy_auto_init_deskew(maxim2c); if (ret) { dev_err(dev, "%s: txphy hw init error\n", __func__); return ret; } return 0; } EXPORT_SYMBOL(maxim2c_mipi_txphy_hw_init); void maxim2c_mipi_txphy_data_init(maxim2c_t *maxim2c) { maxim2c_mipi_txphy_t *mipi_txphy = &maxim2c->mipi_txphy; struct maxim2c_txphy_cfg *phy_cfg = NULL; int i = 0; mipi_txphy->phy_mode = MAXIM2C_TXPHY_MODE_2X4LANES; mipi_txphy->force_clock_out_en = 1; for (i = 0; i < MAXIM2C_TXPHY_ID_MAX; i++) { phy_cfg = &mipi_txphy->phy_cfg[i]; phy_cfg->phy_enable = 0; phy_cfg->phy_type = MAXIM2C_TXPHY_TYPE_DPHY; phy_cfg->auto_deskew = 0; phy_cfg->data_lane_num = 4; phy_cfg->data_lane_map = 0xe4; phy_cfg->vc_ext_en = 0; phy_cfg->tunnel_enable = 0; phy_cfg->clock_master = 0; phy_cfg->clock_mode = MAXIM2C_TXPHY_DPLL_PREDEF; } } EXPORT_SYMBOL(maxim2c_mipi_txphy_data_init);