@@ -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)
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@oss.nxp.com> --- drivers/staging/media/max96712/max96712.c | 79 +++++++++++++++++------ 1 file changed, 60 insertions(+), 19 deletions(-)