The downstream nodes can use the V4L2_CID_PIXEL_RATE control to estimate the link frequency but this can result in innacurate rates. Instead, implement the V4L2_CID_LINK_FREQ control and pass the link frequency from DT. If link-frequency DT property is missing fallback to using the platform info DPLL value to compute the link frequency. Also, remove the pixel rate control since it's not needed anymore. Signed-off-by: Laurentiu Palcu <laurentiu.palcu@xxxxxxxxxxx> --- drivers/staging/media/max96712/max96712.c | 79 +++++++++++++++++------ 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/drivers/staging/media/max96712/max96712.c b/drivers/staging/media/max96712/max96712.c index 9c255979932d6..546660e4b3d1e 100644 --- a/drivers/staging/media/max96712/max96712.c +++ b/drivers/staging/media/max96712/max96712.c @@ -83,7 +83,11 @@ #define T_T3_POST_SHIFT 2 /* MIPI_TX: 0 <= phy < 4 */ -#define MAX96712_MIPI_TX_10(phy) CCI_REG8(0x090a + (phy) * 0x40) +#define MAX96712_MIPI_TX_DESKEW_INIT(phy) CCI_REG8(0x0903 + (phy) * 0x40) +#define DPHY_DESKEW_AUTO_INIT_EN BIT(7) +#define MAX96712_MIPI_TX_DESKEW_PER(phy) CCI_REG8(0x0904 + (phy) * 0x40) +#define PERIODIC_DESKEW_CALIBRATION_EN BIT(7) +#define MAX96712_MIPI_TX_10(phy) (0x090a + (phy) * 0x40) #define CSI2_TWAKEUP_H_MASK GENMASK(2, 0) #define CSI2_TWAKEUP_H_SHIFT 0 #define CSI2_VCX_EN BIT(4) @@ -137,6 +141,8 @@ MAX96712_MAX_TX_PORTS + \ MAX96712_MAX_VPG_PORTS) +#define MHZ(f) ((f) * 1000000U) + enum max96712_pattern { MAX96712_PATTERN_CHECKERBOARD = 0, MAX96712_PATTERN_GRADIENT, @@ -160,6 +166,7 @@ struct max96712_priv { bool cphy; struct v4l2_mbus_config_mipi_csi2 mipi; + s64 link_freq; struct v4l2_subdev sd; struct v4l2_ctrl_handler ctrl_handler; @@ -252,12 +259,28 @@ static void max96712_mipi_configure(struct max96712_priv *priv) PHY_CSI_TX_DPLL_FB_FRACTION_PREDEF_EN | PHY_CSI_TX_DPLL_PREDEF_FREQ_MASK, PHY_CSI_TX_DPLL_FB_FRACTION_PREDEF_EN | - ((priv->info->dpllfreq / 100) & 0x1f)); + (((priv->link_freq * 2) / MHZ(100)) & 0x1f)); max96712_update_bits(priv, MAX96712_BACKTOP0_25, PHY_CSI_TX_DPLL_FB_FRACTION_PREDEF_EN | PHY_CSI_TX_DPLL_PREDEF_FREQ_MASK, PHY_CSI_TX_DPLL_FB_FRACTION_PREDEF_EN | - ((priv->info->dpllfreq / 100) & 0x1f)); + (((priv->link_freq * 2) / MHZ(100)) & 0x1f)); + + /* disable deskew on PHY0 and PHY1 if D-PHY is used and DPLL <= 1500MHz */ + if (!priv->cphy) { + u32 dpll = priv->link_freq * 2; + u8 auto_deskew_en = dpll > MHZ(1500) ? DPHY_DESKEW_AUTO_INIT_EN : 0; + u8 auto_deskew_calib_en = dpll > MHZ(1500) ? PERIODIC_DESKEW_CALIBRATION_EN : 0; + + max96712_update_bits(priv, MAX96712_MIPI_TX_DESKEW_INIT(0), + DPHY_DESKEW_AUTO_INIT_EN, auto_deskew_en); + max96712_update_bits(priv, MAX96712_MIPI_TX_DESKEW_PER(0), + PERIODIC_DESKEW_CALIBRATION_EN, auto_deskew_calib_en); + max96712_update_bits(priv, MAX96712_MIPI_TX_DESKEW_INIT(1), + DPHY_DESKEW_AUTO_INIT_EN, auto_deskew_en); + max96712_update_bits(priv, MAX96712_MIPI_TX_DESKEW_PER(1), + PERIODIC_DESKEW_CALIBRATION_EN, auto_deskew_calib_en); + } /* Enable PHY0 and PHY1 */ max96712_update_bits(priv, MAX96712_MIPI_PHY_2, PHY_STDBY_N_MASK, PHY0_EN | PHY1_EN); @@ -409,7 +432,7 @@ static const struct v4l2_ctrl_ops max96712_ctrl_ops = { static int max96712_v4l2_register(struct max96712_priv *priv) { - long pixel_rate; + struct v4l2_ctrl *link_freq_ctrl; int ret; int i; @@ -420,18 +443,15 @@ static int max96712_v4l2_register(struct max96712_priv *priv) v4l2_ctrl_handler_init(&priv->ctrl_handler, 2); - /* - * TODO: Once V4L2_CID_LINK_FREQ is changed from a menu control to an - * INT64 control it should be used here instead of V4L2_CID_PIXEL_RATE. - */ - pixel_rate = priv->info->dpllfreq / priv->mipi.num_data_lanes * 1000000; - v4l2_ctrl_new_std(&priv->ctrl_handler, NULL, V4L2_CID_PIXEL_RATE, - pixel_rate, pixel_rate, 1, pixel_rate); + v4l2_ctrl_new_int_menu(&priv->ctrl_handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, &priv->link_freq); - v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, &max96712_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(max96712_test_pattern) - 1, - 0, 0, max96712_test_pattern); + link_freq_ctrl = v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, &max96712_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(max96712_test_pattern) - 1, + 0, 0, max96712_test_pattern); + if (link_freq_ctrl) + link_freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; priv->sd.ctrl_handler = &priv->ctrl_handler; ret = priv->ctrl_handler.error; @@ -515,7 +535,7 @@ static int max96712_parse_tx_ports(struct max96712_priv *priv, struct device_nod unsigned int supported_lanes; int ret; - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &v4l2_ep); + ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(node), &v4l2_ep); if (ret) { dev_err(&priv->client->dev, "Could not parse v4l2 endpoint\n"); return -EINVAL; @@ -533,18 +553,39 @@ static int max96712_parse_tx_ports(struct max96712_priv *priv, struct device_nod default: dev_err(&priv->client->dev, "Unsupported bus-type %u\n", v4l2_ep.bus_type); - return -EINVAL; + ret = -EINVAL; + goto free_v4l2_ep; } if (v4l2_ep.bus.mipi_csi2.num_data_lanes != supported_lanes) { dev_err(&priv->client->dev, "Only %u data lanes supported\n", supported_lanes); - return -EINVAL; + ret = -EINVAL; + goto free_v4l2_ep; + } + + if (v4l2_ep.nr_of_link_frequencies != 1) { + dev_info(&priv->client->dev, + "No link frequencies provided in DT, use platform info.\n"); + priv->link_freq = MHZ(priv->info->dpllfreq) / 2; + } else { + priv->link_freq = v4l2_ep.link_frequencies[0]; + + if (priv->link_freq < MHZ(100) || priv->link_freq > MHZ(1250) || + priv->link_freq % MHZ(50)) { + dev_err(&priv->client->dev, + "Link frequency must be a multiple of 50MHz.\n"); + ret = -EINVAL; + goto free_v4l2_ep; + } } priv->mipi = v4l2_ep.bus.mipi_csi2; - return 0; +free_v4l2_ep: + v4l2_fwnode_endpoint_free(&v4l2_ep); + + return ret; } static int max96712_parse_dt(struct max96712_priv *priv) -- 2.44.1