Re: [PATCH 2/2] added support for csirx dphy

From: Maxime Ripard
Date: Thu Jun 14 2018 - 03:51:35 EST


Hi,

On Fri, Jun 08, 2018 at 11:33:04AM +0100, Krzysztof Witos wrote:
> Signed-off-by: Krzysztof Witos <kwitos@xxxxxxxxxxx>

A commit log explaining what you're doing here would be nice.

> ---
> drivers/media/platform/cadence/cdns-csi2rx.c | 342 ++++++++++++++++++++++++---
> 1 file changed, 313 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
> index a0f02916006b..9251ea6015f0 100644
> --- a/drivers/media/platform/cadence/cdns-csi2rx.c
> +++ b/drivers/media/platform/cadence/cdns-csi2rx.c
> @@ -2,14 +2,16 @@
> /*
> * Driver for Cadence MIPI-CSI2 RX Controller v1.3
> *
> - * Copyright (C) 2017 Cadence Design Systems Inc.
> + * Copyright (C) 2017,2018 Cadence Design Systems Inc.
> */
>
> #include <linux/clk.h>
> +#include <linux/iopoll.h>
> #include <linux/delay.h>
> #include <linux/io.h>
> #include <linux/module.h>
> #include <linux/of.h>
> +#include <linux/of_address.h>
> #include <linux/of_graph.h>
> #include <linux/phy/phy.h>
> #include <linux/platform_device.h>
> @@ -44,6 +46,33 @@
> #define CSI2RX_LANES_MAX 4
> #define CSI2RX_STREAMS_MAX 4
>
> +/* DPHY registers */
> +#define DPHY_PMA_CMN(reg) (reg)
> +#define DPHY_PMA_LCLK(reg) (0x100 + (reg))
> +#define DPHY_PMA_LDATA(lane, reg) (0x200 + ((lane) * 0x100) + (reg))
> +#define DPHY_PMA_RCLK(reg) (0x600 + (reg))
> +#define DPHY_PMA_RDATA(lane, reg) (0x700 + ((lane) * 0x100) + (reg))
> +#define DPHY_PCS(reg) (0xb00 + (reg))
> +
> +#define DPHY_CMN_SSM DPHY_PMA_CMN(0x20)
> +#define DPHY_CMN_SSM_EN BIT(0)
> +#define DPHY_CMN_RX_MODE_EN BIT(10)
> +
> +#define DPHY_CMN_PWM DPHY_PMA_CMN(0x40)
> +#define DPHY_CMN_PWM_DIV(x) ((x) << 20)
> +#define DPHY_CMN_PWM_LOW(x) ((x) << 10)
> +#define DPHY_CMN_PWM_HIGH(x) (x)
> +
> +#define DPHY_CMN_PLL_CFG DPHY_PMA_CMN(0xE8)
> +#define PLL_LOCKED BIT(2)
> +
> +#define DPHY_PSM_CFG DPHY_PCS(0x4)
> +#define DPHY_PSM_CFG_FROM_REG BIT(0)
> +#define DPHY_PSM_CLK_DIV(x) ((x) << 1)
> +
> +#define DPHY_BAND_CTRL DPHY_PCS(0x0)
> +#define DPHY_BAND_LEFT_VAL(x) (x)
> +
> enum csi2rx_pads {
> CSI2RX_PAD_SINK,
> CSI2RX_PAD_SOURCE_STREAM0,
> @@ -67,7 +96,7 @@ struct csi2rx_priv {
> struct clk *sys_clk;
> struct clk *p_clk;
> struct clk *pixel_clk[CSI2RX_STREAMS_MAX];
> - struct phy *dphy;
> + struct clk *hs_clk;
>
> u8 lanes[CSI2RX_LANES_MAX];
> u8 num_lanes;
> @@ -83,8 +112,175 @@ struct csi2rx_priv {
> struct v4l2_async_subdev asd;
> struct v4l2_subdev *source_subdev;
> int source_pad;
> + struct cdns_dphy *dphy;
> +};
> +
> +struct cdns_dphy_cfg {
> + unsigned int nlanes;
> +};
> +
> +struct cdns_dphy;
> +
> +enum cdns_dphy_clk_lane_cfg {
> + DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
> + DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
> + DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
> + DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3
> +};
> +
> +struct cdns_dphy_ops {
> + int (*probe)(struct cdns_dphy *dphy);
> + void (*remove)(struct cdns_dphy *dphy);
> + void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
> + void (*set_pll_cfg)(struct cdns_dphy *dphy);
> + void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
> + enum cdns_dphy_clk_lane_cfg cfg);
> + void (*is_pll_locked)(struct cdns_dphy *dphy);
> + void (*set_band_ctrl)(struct cdns_dphy *dphy, u8 value);
> +};
> +
> +struct cdns_dphy {
> + struct cdns_dphy_cfg cfg;
> + void __iomem *regs;
> + struct clk *psm_clk;
> + const struct cdns_dphy_ops *ops;
> };
>
> +static int cdns_dphy_set_band_ctrl(struct cdns_dphy *dphy,
> + struct csi2rx_priv *csirx)
> +{
> + u8 band_value;
> + u32 hs_freq_mhz = clk_get_rate(csirx->hs_clk);
> +
> + if (hs_freq_mhz >= 80 && hs_freq_mhz < 100)
> + band_value = 0;
> + else if (hs_freq_mhz >= 100 && hs_freq_mhz < 120)
> + band_value = 1;
> + else if (hs_freq_mhz >= 120 && hs_freq_mhz < 160)
> + band_value = 2;
> + else if (hs_freq_mhz >= 160 && hs_freq_mhz < 200)
> + band_value = 3;
> + else if (hs_freq_mhz >= 200 && hs_freq_mhz < 240)
> + band_value = 4;
> + else if (hs_freq_mhz >= 240 && hs_freq_mhz < 280)
> + band_value = 5;
> + else if (hs_freq_mhz >= 280 && hs_freq_mhz < 320)
> + band_value = 6;
> + else if (hs_freq_mhz >= 320 && hs_freq_mhz < 360)
> + band_value = 7;
> + else if (hs_freq_mhz >= 360 && hs_freq_mhz < 400)
> + band_value = 8;
> + else if (hs_freq_mhz >= 400 && hs_freq_mhz < 480)
> + band_value = 9;
> + else if (hs_freq_mhz >= 480 && hs_freq_mhz < 560)
> + band_value = 10;
> + else if (hs_freq_mhz >= 560 && hs_freq_mhz < 640)
> + band_value = 11;
> + else if (hs_freq_mhz >= 640 && hs_freq_mhz < 720)
> + band_value = 12;
> + else if (hs_freq_mhz >= 720 && hs_freq_mhz < 800)
> + band_value = 13;
> + else if (hs_freq_mhz >= 800 && hs_freq_mhz < 880)
> + band_value = 14;
> + else if (hs_freq_mhz >= 880 && hs_freq_mhz < 1040)
> + band_value = 15;
> + else if (hs_freq_mhz >= 1040 && hs_freq_mhz < 1200)
> + band_value = 16;
> + else if (hs_freq_mhz >= 1200 && hs_freq_mhz < 1350)
> + band_value = 17;
> + else if (hs_freq_mhz >= 1350 && hs_freq_mhz < 1500)
> + band_value = 18;
> + else if (hs_freq_mhz >= 1500 && hs_freq_mhz < 1750)
> + band_value = 19;
> + else if (hs_freq_mhz >= 1750 && hs_freq_mhz < 2000)
> + band_value = 20;
> + else if (hs_freq_mhz >= 2000 && hs_freq_mhz < 2250)
> + band_value = 21;
> + else if (hs_freq_mhz >= 2250 && hs_freq_mhz <= 2500)
> + band_value = 22;
> + else
> + return -EINVAL;
> +
> + if (dphy->ops->set_band_ctrl)
> + dphy->ops->set_band_ctrl(dphy, band_value);
> +
> + return 0;
> +}
> +
> +static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
> +{
> + unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
> + unsigned long psm_div;
> +
> + if (!psm_clk_hz || psm_clk_hz > 100000000)
> + return -EINVAL;
> +
> + psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
> + if (dphy->ops->set_psm_div)
> + dphy->ops->set_psm_div(dphy, psm_div);
> +
> + return 0;
> +}
> +
> +static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
> + enum cdns_dphy_clk_lane_cfg cfg)
> +{
> + if (dphy->ops->set_clk_lane_cfg)
> + dphy->ops->set_clk_lane_cfg(dphy, cfg);
> +}
> +
> +static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy)
> +{
> + if (dphy->ops->set_pll_cfg)
> + dphy->ops->set_pll_cfg(dphy);
> +}
> +
> +static void cdns_dphy_is_pll_locked(struct cdns_dphy *dphy)
> +{
> + if (dphy->ops->is_pll_locked)
> + dphy->ops->is_pll_locked(dphy);
> +}
> +
> +static void cdns_csirx_dphy_init(struct csi2rx_priv *csi2rx,
> + const struct cdns_dphy_cfg *dphy_cfg)
> +{
> +
> + /*
> + * Configure the band control settings.
> + */
> + cdns_dphy_set_band_ctrl(csi2rx->dphy, csi2rx);
> +
> + /*
> + * Configure the internal PSM clk divider so that the DPHY has a
> + * 1MHz clk (or something close).
> + */
> + WARN_ON_ONCE(cdns_dphy_setup_psm(csi2rx->dphy));
> +
> + /*
> + * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
> + * and 8 data lanes, each clk lane can be attache different set of
> + * data lanes. The 2 groups are named 'left' and 'right', so here we
> + * just say that we want the 'left' clk lane to drive the 'left' data
> + * lanes.
> + */
> + cdns_dphy_set_clk_lane_cfg(csi2rx->dphy,
> + DPHY_CLK_CFG_LEFT_DRIVES_LEFT);
> +
> + /*
> + * Configure the DPHY PLL that will be used to generate the TX byte
> + * clk.
> + */
> + cdns_dphy_set_pll_cfg(csi2rx->dphy);
> +
> + /* Start RX state machine. */
> + writel(DPHY_CMN_SSM_EN | DPHY_CMN_RX_MODE_EN,
> + csi2rx->dphy->regs + DPHY_CMN_SSM);
> +
> + /* Checking if PLL is locked */
> + cdns_dphy_is_pll_locked(csi2rx->dphy);
> +
> +}
> +

That part looks like it's pretty much the same thing than the PHY
support in the DSI bridge found there:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/bridge/cdns-dsi.c#n493

This code shouldn't be duplicated, but shared, ideally through the phy
framework. That would require some changes to that framework though.

Maxime

--
Maxime Ripard, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

Attachment: signature.asc
Description: PGP signature