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